Search code examples
hamcrestassertj

Test that either one thing holds or another in AssertJ


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.


Solution

  • 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.