21

I'd like to assert that List<Achievement> contains a member of type TestAchievement.

Here's my assertion:

List<Achievement> achievements; // Populated elsewhere
assertThat(achievements,hasItem(isA(TestAchievement.class)));

This doesn't compile, reporting the error:

The method assertThat(T, Matcher) in the type Assert is not applicable for the arguments (List, Matcher<Iterable<TestAchievement>>)

What's the correct syntax for this type of assertion using Hamcrest?

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
Marty Pitt
  • 28,822
  • 36
  • 122
  • 195

7 Answers7

22

Thanks for all the help.

The posts here suggested it was a defect with Hamcrest, so I headed over to the hacmrest site to register a bug, whien I discovered that the mvn / ivy dependency declaration I was using was out-of-date, giving me an old version of Hamcrest.

This bug exists with 1.1, which is the latest if declared using

<dependency org="org.hamcrest" name="hamcrest-all" rev="1.1">

However, the correct depedency declaration is:

<dependency org="org.hamcrest" name="hamcrest-library" rev="1.3.RC2"/>

Updating to this solved the issue. The syntax used in my test is:

 assertThat(achievements, hasItem(isA(TestAchievement.class)));
Marty Pitt
  • 28,822
  • 36
  • 122
  • 195
  • 2
    I spent nearly a week of headbanging once on this very same problem of a buggy declaration in the hamcrest library code before I finally stumbled on the fixed updated version. Just nuts! Things should not be this complicated. – tchrist Feb 13 '11 at 03:26
9

There is a bug in Java 6 that relates to this.

This code will throw various errors such as "cannot find symbol..."

assertThat(achievements, hasItem(isA(TestAchievement.class)));

The work around for this is to declare the matcher as a variable and then reference that variable. It is important to note that the type of the variable, specifically the generics section, is very important for this to work.

Matcher<Iterable<? super TestAchievement>> matcher = hasItem(isA(TestAchievement.class));
assertThat(achievements, matcher);

Interestingly if you use instanceOf() instead of isA() you once again run into issue. (although if you ignore the warnings this might just work for you anyway.) Expanding on the previous fix you can use:

Matcher<TestAchievement> itemMatcher = Matchers.instanceOf(TestAchievement.class);
Matcher<Iterable<? super TestAchievement>> matcher = hasItem(itemMatcher);
assertThat(achievements, matcher);
Rylander
  • 19,449
  • 25
  • 93
  • 144
  • I typically add the comment `// work around for Java 6 Bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7034548` whenever this comes up. – Rylander Dec 23 '13 at 21:39
3

I've been battling with this for a while, none of the approaches here really had the expected result. Out of pure despair I tried converting to an array to see if that made a difference:

List<Object> listOfThings = (List) someService.findThings(); // your business logic here
Object[] arrayOfThings  = listOfThings.toArray(new Object[listOfThings.size()]);

assertThat(arrayOfThings, hasItemInArray(instanceOf(SpecialThing.class)));

And it did make a difference. Now my test works flawlessly. The one line of copy-to-array seems to be a reasonable trade-off to save a lot of hassle.

joerx
  • 2,028
  • 1
  • 16
  • 18
  • If you want to avoid using primitive arrays see [this answer](http://stackoverflow.com/questions/4981586/java-hamcrest-collection-contains-item-of-type/20751480#20751480). (You will still have the one extra line though.) – Rylander Dec 23 '13 at 21:42
3
assertThat(achievements, hasItem(
    IsInstanceOf.<Achievement>instanceOf(TestAchievement.class)));
NamshubWriter
  • 23,549
  • 2
  • 41
  • 59
  • That line doesn't compile. Here's the error message: `The method instanceOf(Class>) of type IsInstanceOf is not generic; it cannot be parameterized with arguments ` – limc Feb 13 '11 at 03:01
  • @limc It's generic in the code at the trunk: http://code.google.com/p/hamcrest/source/browse/trunk/hamcrest-java/hamcrest-core/src/main/java/org/hamcrest/core/IsInstanceOf.java – NamshubWriter Feb 13 '11 at 15:12
2

Actuall it does not work for a little bit more complex Matcher, even using latest release.

assertThat(result.getAnswers(),
            Matchers.hasItem(Matchers.hasProperty("content")));

It will not work, you will get a:

The method assertThat(T, Matcher) in the type Assert is not applicable for the arguments (Set, Matcher>)

You can solve it by converting it to an array :

assertThat(result.getAnswers().toArray(), hasItemInArray(Matchers.hasProperty("content", equalTo("<h4>new comment</h4>"))));
Hugo Dozois
  • 8,147
  • 12
  • 54
  • 58
Ark Xu
  • 111
  • 1
  • 4
2

I have been futzing around with this for awhile, and it seems like the only way I know is to convert List<Achievement> to List<Object>. The problem is CoreMatchers.instanceOf() returns Matcher<Object>.

With that modification, I'm able to get this to work:-

List<Object> achievements = new ArrayList<Object>();
achievements.add(new Achievement());
achievements.add(new TestAchievement());
assertThat(achievements, hasItem(instanceOf(TestAchievement.class)));
limc
  • 39,366
  • 20
  • 100
  • 145
2

From http://code.google.com/p/hamcrest/source/browse/trunk/hamcrest-java/hamcrest-core/src/main/java/org/hamcrest/MatcherAssert.java the signature is

assertThat(T actual, Matcher<? super T> matcher)

so the problem is that your matcher is a Matcher<TestAchievement>, not a matcher that works for any instance of a super-class or interface of achievement.

It looks like the instanceOf matcher just has a buggy type bound. You can work around that bug by doing something like

@SuppressWarnings("unchecked")
Matcher/*no_param*/ isATestAchievement = instanceOf(TestAchievement.class);
assertThat(..., isATestAchievement);
Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • This is a good example. Works for me but only on hamcrest 1.1. Upgrading to 1.2 or 1.3.0RC0 will give me n error. – Jesper Rønn-Jensen Mar 17 '11 at 10:55
  • The error (for 1.2) stacktrace: java.lang.NoSuchMethodError: org.hamcrest.Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V at org.hamcrest.core.IsCollectionContaining.matchesSafely(IsCollectionContaining.java:31) at org.hamcrest.core.IsCollectionContaining.matchesSafely(IsCollectionContaining.java:14) at org.hamcrest.TypeSafeDiagnosingMatcher.matches(TypeSafeDiagnosingMatcher.java:54) at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:12) at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8) – Jesper Rønn-Jensen Mar 17 '11 at 10:56
  • @JesperRønn-Jensen See [this answer](http://stackoverflow.com/questions/4981586/java-hamcrest-collection-contains-item-of-type/20751480#20751480). This works with hamcrest 1.1 and 1.3. – Rylander Dec 23 '13 at 21:46