55

Why does this not compile, oh, what to do?

import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItems;

ArrayList<Integer> actual = new ArrayList<Integer>();
ArrayList<Integer> expected = new ArrayList<Integer>();
actual.add(1);
expected.add(2);
assertThat(actual, hasItems(expected));

error copied from comment:

cannot find symbol method assertThat(java.util.ArrayList<java.lang.Integer>, org.hamcreset.Matcher<java.lang.Iterable<java.util.ArrayList<java.lang.Integer>>>)
Steve Chambers
  • 37,270
  • 24
  • 156
  • 208
ripper234
  • 222,824
  • 274
  • 634
  • 905

9 Answers9

55

Just ran into this post trying to fix it for myself. Gave me just enough information to work it out.

You can give the compiler just enough to persuade it to compile by casting the return value from hasItems to a (raw) Matcher, eg:

ArrayList<Integer> actual = new ArrayList<Integer>();
ArrayList<Integer> expected = new ArrayList<Integer>();
actual.add(1);
expected.add(2);
assertThat(actual, (Matcher) hasItems(expected));

Just in case anybody else is still suffering ...

Edit to add: In spite of the up votes, this answer is wrong, as Arend points out below. The correct answer is to turn the expected into an array of Integers, as hamcrest is expecting:

    ArrayList<Integer> actual = new ArrayList<Integer>();
    ArrayList<Integer> expected = new ArrayList<Integer>();
    actual.add(1);
    expected.add(2);
    assertThat(actual, hasItems(expected.toArray(new Integer[expected.size()])));
Clive Evans
  • 658
  • 8
  • 12
  • I haven't tested this answer since I long ago have lost this source code (moved companies twice), but I gave you a thumb up for resurrecting this. – ripper234 Sep 07 '11 at 13:53
  • 1
    Good tip. Seems the only "straightforward" way to use assertThat() with any collection matcher since Hamcrest changed their collection matcher generics to extends T> instead of just .. – Frans Jan 11 '12 at 14:48
  • I wished `hasItems` also matched the order. Which it seems like it does not. – Amir Raminfar Apr 12 '12 at 21:26
  • `hasItems` is used to assert that the collection has the items in question, it does not require that it has no other items or the order. You probably want to look at `contains`, which ensures that the collection has all of the supplied items, in order, and nothing else. – Clive Evans Apr 14 '12 at 07:55
  • 19
    This is **WRONG**. This answer checks if `actual` contains the List `expected`, not if `actual` contains the elements of the List `expected`. Try it with `expected.add(1)` instead: This code will never pass. There's a very good reason for the compiler error. Not a good idea to just suppress it with a cast. – Arend v. Reinersdorff Jun 19 '12 at 08:38
  • 2
    Yeees, which given I went on and got the test passing is confusing, to say the least. The _correct_ answer, is: ArrayList actual = new ArrayList(); ArrayList expected = new ArrayList(); actual.add(1); expected.add(2); assertThat(actual, hasItems(expected.toArray(new Integer[expected.size()]))); – Clive Evans Jun 28 '12 at 14:42
  • This post is a disaster. Please update it or remove it. – Alex Worden May 24 '16 at 20:45
  • How so? The edited answer is as correct now as it was when it was edited. – Clive Evans May 25 '16 at 14:58
22

hasItems checks that a collection contains some items, not that 2 collections are equal, just use the normal equality assertions for that. So either assertEquals(a, b) or using assertThat

import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.is;

ArrayList<Integer> actual = new ArrayList<Integer>();
ArrayList<Integer> expected = new ArrayList<Integer>();
actual.add(1);
expected.add(2);
assertThat(actual, is(expected));

Alternatively, use the contains Matcher, which checks that an Iterable contains items in a specific order

import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.contains;

ArrayList<Integer> actual = new ArrayList<Integer>();
actual.add(1);
actual.add(2);
assertThat(actual, contains(1, 2)); // passes
assertThat(actual, contains(3, 4)); // fails

If you don't care about the order use containsInAnyOrder instead.

evandrix
  • 6,041
  • 4
  • 27
  • 38
Dan Godfrey
  • 456
  • 3
  • 6
12

You are comparing ArrayList<Integer> with int. The correct comparison is:

...
assertThat(actual, hasItem(2));

-- Edit --

I'm sorry, I've read it wrong. Anyway, the signature of hasItems you want is:

public static <T> org.hamcrest.Matcher<java.lang.Iterable<T>> hasItems(T... elements)

i.e., it accepts a variable number of arguments. I'm not sure if an ArrayList<T> is compatible, just guessing here. Try sending each item from the expected list interspersed by comma.

assertThat(actual, hasItems(2,4,1,5,6));

-- Edit 2 --

Just pasting here my comment, there is an equivalent expression for what you want, without using Hamcrest:

assertTrue(actual.containsAll(expected));
freitass
  • 6,542
  • 5
  • 40
  • 44
  • I'm comparing multiple items. (hasItems, not hasItem) – ripper234 Jul 07 '09 at 15:32
  • So, hamcrest simply does not support comparing two collections? (I don't have an actual list of items, I'm building the list of numbers from 0 to 999 I must compare collections). – ripper234 Jul 07 '09 at 18:29
  • I cannot answer that for you, I'm telling this based on the documentation. But there is a simpler way to do what you want... use assertTrue(actual.containsAll(expected)). – freitass Jul 07 '09 at 19:57
  • 3
    it's simpler, but when this assert fails you get a very cryptic error messages, which is less helpful than the error message that I imagine hasItems outputs. – ripper234 Oct 13 '10 at 10:06
3

Try

assertThat(actual, hasItems(expected.toArray(new Integer[0])));

to satisfy the matcher signature. No Eclipse around, so this might not work.

Robert Munteanu
  • 67,031
  • 36
  • 206
  • 278
2

That error message looks like one produced by the javac compiler. I've found in the past that code written using hamcrest just won't compile under javac. The same code will compile fine under, say, the Eclipse compiler.

I think Hamcrest's generics are exercising corner cases in generics that javac can't deal with.

skaffman
  • 398,947
  • 96
  • 818
  • 769
2

You can get this error if you try to replace jUnit's hamcrest with a newer version. For example, using junit-dep together with hamcrest 1.3 requires that use assertThat from hamcrest instead of jUnit.

So the solution is to use

import static org.hamcrest.MatcherAssert.assertThat;

instead of

import static org.junit.Assert.assertThat;

Mika
  • 1,256
  • 13
  • 18
2

I just came across the same problem and the following trick worked for me:

  • use import static org.hamcrest.Matchers.hasItems
  • have the hamcrest library before junit in classpath (build path -> order and export)
Zsolt
  • 1,464
  • 13
  • 22
1

For these cases when code does compile in Eclipse but javac shows errors please do help hamcrest by providing explicitly type parameter e.g. Matchers.hasItem()

miluch
  • 1
  • 1
0
ArrayList<Integer> expected = new ArrayList<Integer>();
expected.add(1);
expected.add(2);
hasItems(expected);

hasItems(T..t) is being expanded by the compiler to:

hasItems(new ArrayList<Integer>[]{expected});

You are passing a single element array containing an ArrayList. If you change the ArrayList to an Array, then your code will work.

Integer[] expected = new Integer[]{1, 2};
hasItems(expected);

This will be expanded to:

hasItems(1, 2);
User109377
  • 11
  • 4