2

Let's say I have Strings "foo", "bar" and baz

and that I'm given a Stream<String[]> candidates.

I now want to assertThat all elements in candidates are tuples containing either {"foo", "bar"} (in any order) or {"bar", "baz"} (in any order).

How do I best do that?

User1291
  • 7,664
  • 8
  • 51
  • 108

3 Answers3

1

You can use allMatch or allSatisfy something like

assertThat(candidates).allMatch(candidate -> candidate.contains...)

or

assertThat(candidates).allSatisfy(
          candidate -> assertThat(candidate).satisfiesAnyOf(
                       c -> {      
                          c.contains("foo");
                          c.contains("bar");
                       },
                       c -> {      
                          c.contains("bar");
                          c.contains("baz");
                       })                                     
         );
Joel Costigliola
  • 6,308
  • 27
  • 35
0

The first step is to use Stream#allMatch(Predicate) to check the tuple for either the combination of [foo, bar] or [bar, baz] and the size of the tuple.

In the second step I have converted the inner array into a Set<String> which makes it easy to resolve cases like [foo, foo] as the duplicates would be removed and also we can use Set.contains() for a O(1) lookup to check either [foo, bar] or [bar, baz] is present in the stream of tuples:

class TupleTest {

@org.junit.jupiter.api.Test
void testTupleOfCandidates_True() {
    Stream<String[]> candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"});
    assertTrue(isCandidateStreamValid(candidates));

    candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"});
    assertTrue(isCandidateStreamValid(candidates));

}

@org.junit.jupiter.api.Test
void testTupleOfCandidates_False() {
    Stream<String[]> candidates = Stream.of(new String[]{"foo", "foo"}, new String[]{"bar", "baz"});
    assertFalse(isCandidateStreamValid(candidates));

    candidates = Stream.of(new String[]{"bar", "foo"}, new String[]{"bar", "baz"}, new String[]{"baz", "bar"});
    assertTrue(isCandidateStreamValid(candidates));
}

public boolean isCandidateStreamValid(Stream<String[]> candidates){
        return candidates.allMatch(arr -> {
            Set<String> data = new HashSet(Arrays.asList(arr));
            return data.size() == 2
                    &&
                    ((data.contains("foo") && data.contains("bar"))
                            ||
                            (data.contains("bar") && data.contains("baz")));
        });
    }
}
Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
0

For simplicity, I'd create a function that can validate a tuple. This function will be called on each element in the stream. I would also create a second function that can check whether an array contains each element of another array. Something like this:

import org.junit.Test;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;

public class MyTest {

    @Test
    public void test(){
        Stream<String[]> stream = Stream.of(new String[]{"foo", "bar"}, new String[]{"bar", "baz"});
        stream.forEach(arr -> assertThat(validTuple(arr)).isTrue());

        //Order does not matter:
        stream = Stream.of(new String[]{"bar", "foo"}, new String[]{"baz", "bar"});
        stream.forEach(arr -> assertThat(validTuple(arr)).isTrue());

        //Invalid tuples
        stream = Stream.of(new String[]{"foo", "asdf"}, new String[]{"foo", "baz"});
        stream.forEach(arr -> assertThat(validTuple(arr)).isFalse());
    }

    private boolean validTuple(String[] array){
        if(array.length != 2) return false;
        return containsAll(array, "foo", "bar") || containsAll(array, "bar", "baz");
    }

    private boolean containsAll(String[] array, String... values){
        for (String value : values) {
            boolean containsValue = false;
            for (String s : array) {
                if(s.equals(value)){
                    containsValue = true;
                    break;
                }
            }
            if(!containsValue){
                return false;
            }
        }

        return true;
    }

}

You can now easily extract those methods to a dedicated class so you can re-use them in other tests if need be.

Pasukaru
  • 1,050
  • 1
  • 10
  • 22
  • If we're going down that route, I suggest making your function a `Predicate` and using `stream.allMatch(validTuple)`. – User1291 Aug 26 '19 at 11:50