21

I am in the process of converting some tests from Hamcrest to AssertJ. In Hamcrest I use the following snippet:

assertThat(list, either(contains(Tags.SWEETS, Tags.HIGH))
    .or(contains(Tags.SOUPS, Tags.RED)));

That is, the list may be either that or that. How can I express this in AssertJ? The anyOf function (of course, any is something else than either, but that would be a second question) takes a Condition; I have implemented that myself, but it feels as if this should be a common case.

Michael Piefel
  • 18,660
  • 9
  • 81
  • 112

2 Answers2

29

Edited:

Since 3.12.0 AssertJ provides satisfiesAnyOf which succeeds if at least one of the given assertion succeeds,

assertThat(list).satisfiesAnyOf(
    listParam -> assertThat(listParam).contains(Tags.SWEETS, Tags.HIGH),
    listParam -> assertThat(listParam).contains(Tags.SOUPS, Tags.RED)
);

Original answer:

No, this is an area where Hamcrest is better than AssertJ.

To write the following assertion:

Set<String> goodTags = newLinkedHashSet("Fine", "Good");
Set<String> badTags = newLinkedHashSet("Bad!", "Awful");
Set<String> tags = newLinkedHashSet("Fine", "Good", "Ok", "?");

// contains is statically imported from ContainsCondition
// anyOf succeeds if one of the conditions is met (logical 'or') 
assertThat(tags).has(anyOf(contains(goodTags), contains(badTags)));

you need to create this Condition:

import static org.assertj.core.util.Lists.newArrayList;    
import java.util.Collection;    
import org.assertj.core.api.Condition;

public class ContainsCondition extends Condition<Iterable<String>> {
  private Collection<String> collection;
  
  public ContainsCondition(Iterable<String> values) {
    super("contains " + values);
    this.collection = newArrayList(values);
  }
  
  static ContainsCondition contains(Collection<String> set) {
    return new ContainsCondition(set);
  }

  @Override
  public boolean matches(Iterable<String> actual) {
    Collection<String> values = newArrayList(actual);
    for (String string : collection) {
      if (!values.contains(string)) return false;
    }
    return true;
  };
}

It might not be what you if you expect that the presence of your tags in one collection implies they are not in the other one.

Joel Costigliola
  • 6,308
  • 27
  • 35
  • That’s almost the same code I used. It’s a bit cumbersome. The `Condition` is very similar to the Hamcrest `Matcher`; the difference is that Hamcrest comes with dozens of them (since that is Hamcrest’s concept, after all). Also, the Hamcrest matchers are really quite versatile, they can be used for anything and can be passed around. That is the price you have to pay for the code completion. Perhaps it would be possible to use a Hamcrest matcher in place of a condition? – Michael Piefel Nov 07 '14 at 07:31
  • 1
    I agree the code is cumbersome. I was thinking about allowing reusing hamcrest matcher in place of Condition, I'm a bit reluctant because it's a different approach that I'm not a big fan of as I find difficult the discover what is the right macther, but in cases like yours it's a good solution. – Joel Costigliola Nov 08 '14 at 00:28
  • AssertJ 3.9.0 includes support for easily converting Hamcrest matchers into AssertJ conditions: http://joel-costigliola.github.io/assertj/assertj-core-news.html#assertj-core-2.9.0-hamcrestMatcherAsCondition – Michael Piefel May 25 '18 at 13:06
3

Inspired by this thread, you might want to use this little repo I put together, that adapts the Hamcrest Matcher API into AssertJ's Condition API. Also includes a handy-dandy conversion shell script.

Dan Haywood
  • 2,215
  • 2
  • 17
  • 23