In fact, you must implements your own Condition
in assertj for checking the collection containing the substrings in order. for example:
assertThat(items).has(containsExactly(
stream(subItems).map(it -> containsSubstring(it)).toArray(Condition[]::new)
));
What's approach did I choose to meet your requirements? write a contract test case, and then implements the feature that the assertj doesn't given, here is my test case for the hamcrest contains(containsString(...))
adapt to assertj containsExactly
as below:
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.util.Collection;
import java.util.List;
import static java.util.Arrays.asList;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toList;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
@RunWith(Parameterized.class)
public class MatchersTest {
private final SubstringExpectation expectation;
public MatchersTest(SubstringExpectation expectation) {
this.expectation = expectation;
}
@Parameters
public static List<SubstringExpectation> parameters() {
return asList(MatchersTest::hamcrest, MatchersTest::assertj);
}
private static void assertj(Collection<? extends String> items, String... subItems) {
Assertions.assertThat(items).has(containsExactly(stream(subItems).map(it -> containsSubstring(it)).toArray(Condition[]::new)));
}
private static Condition<String> containsSubstring(String substring) {
return new Condition<>(s -> s.contains(substring), "contains substring: \"%s\"", substring);
}
@SuppressWarnings("unchecked")
private static <C extends Condition<? super T>, T extends Iterable<? extends E>, E> C containsExactly(Condition<E>... conditions) {
return (C) new Condition<T>("contains exactly:" + stream(conditions).map(it -> it.toString()).collect(toList())) {
@Override
public boolean matches(T items) {
int size = 0;
for (E item : items) {
if (!matches(item, size++)) return false;
}
return size == conditions.length;
}
private boolean matches(E item, int i) {
return i < conditions.length && conditions[i].matches(item);
}
};
}
private static void hamcrest(Collection<? extends String> items, String... subItems) {
assertThat(items, contains(stream(subItems).map(Matchers::containsString).collect(toList())));
}
@Test
public void matchAll() {
expectation.checking(asList("foo", "bar"), "foo", "bar");
}
@Test
public void matchAllContainingSubSequence() {
expectation.checking(asList("foo", "bar"), "fo", "ba");
}
@Test
public void matchPartlyContainingSubSequence() {
try {
expectation.checking(asList("foo", "bar"), "fo");
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage(), containsString("\"bar\""));
}
}
@Test
public void matchAgainstWithManySubstrings() {
try {
expectation.checking(asList("foo", "bar"), "fo", "ba", "<many>");
fail();
} catch (AssertionError expected) {
assertThat(expected.getMessage(), containsString("<many>"));
}
}
private void fail() {
throw new IllegalStateException("should failed");
}
interface SubstringExpectation {
void checking(Collection<? extends String> items, String... subItems);
}
}
However, you down to use chained Condition
s rather than the assertj fluent api, so I suggest you to try use the hamcrest
instead. in other words, if you use this style in assertj you must write many Condition
s or adapt hamcrest Matcher
s to assertj Condition
.