我有一个有效的hamcrest断言:
assertThat(mylist, contains(
containsString("15"),
containsString("217")));
预期的行为是:
mylist == asList("Abcd15", "217aB")
=>成功myList == asList("Abcd15", "218")
=>失败如何将此表达式迁移到assertJ。当然存在天真的解决方案,比如在第一个和第二个值上断言,如下所示:
assertThat(mylist.get(0)).contains("15");
assertThat(mylist.get(1)).contains("217");
但这些是列表元素上的断言,而不是列表中的断言。在列表中尝试断言限制了我非常通用的功能。所以也许它只能通过自定义断言来解决,类似下面的内容就好了:
assertThat(mylist).elements()
.next().contains("15")
.next().contains("217")
但是在我写一个自定义断言之前,我会对其他人如何解决这个问题感兴趣?
编辑:一个额外的非功能性要求是,测试应该可以通过其他约束轻松扩展。在Hamcrest中,很容易表达其他约束,例如:
assertThat(mylist, contains(
emptyString(), //additional element
allOf(containsString("08"), containsString("15")), //extended constraint
containsString("217"))); // unchanged
依赖于列表索引的测试必须为此示例重新编号,使用自定义条件的测试必须重写完整条件(请注意allOf
中的约束不限于子字符串检查)。
答案 0 :(得分:2)
您可以执行以下操作:
int
它给出的错误是:
a
答案 1 :(得分:1)
我找到的最接近的是写一个" ContainsSubstring" condition和一个静态方法来创建一个,并使用
assertThat(list).has(containsSubstring("15", atIndex(0)))
.has(containsSubstring("217", atIndex(1)));
但也许你应该简单地写一个循环:
List<String> list = ...;
List<String> expectedSubstrings = Arrays.asList("15", "217");
for (int i = 0; i < list.size(); i++) {
assertThat(list.get(i)).contains(expectedSubstrings.get(i));
}
或编写参数化测试,以便JUnit本身在每个子字符串上测试每个元素。
答案 2 :(得分:1)
实际上,您必须在assertj中实现自己的Condition
,以便按顺序检查包含子字符串的集合。例如:
assertThat(items).has(containsExactly(
stream(subItems).map(it -> containsSubstring(it)).toArray(Condition[]::new)
));
我选择哪种方法来满足您的要求?编写一个契约测试用例,然后实现assertj没有给出的功能,这是我的测试用例,适用于contains(containsString(...))
适应assertj containsExactly
,如下所示:
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);
}
}
但是,你要使用链式Condition
而不是断言流畅的api,所以我建议你尝试使用hamcrest
。换句话说,如果在assertj中使用此样式,则必须编写许多Condition
s或将hamcrest Matcher
改为assertj Condition
。
答案 3 :(得分:1)
对于这种断言,Hamcrest优于AssertJ,你可以用条件模仿Hamcrest但你需要编写它们,因为在AssertJ中没有开箱即用(断言philosphy在这方面不与Hamcrest竞争)
在下一个AssertJ版本(即将发布!)中,您将能够重用Hamcrest Matcher来构建AssertJ条件,例如:
name description
cflinuxfs2 Cloud Foundry Linux-based filesystem
最后请注意,这个建议......
Condition<String> containing123 = new HamcrestCondition<>(containsString("123"));
// assertions succeed
assertThat("abc123").is(containing123);
assertThat("def456").isNot(containing123);
...遗憾的是由于泛型限制无法工作,虽然你知道你有一个String的列表,但Java泛型的功能不足以根据另一个选择特定类型(assertThat(mylist).elements()
.next().contains("15")
.next().contains("217")
)({ {1}}),这意味着您只能对元素执行StringAssert
断言,而不能String
断言。
答案 4 :(得分:0)
您可以使用anyMatch
assertThat(mylist)
.anyMatch(item -> item.contains("15")
.anyMatch(item -> item.contains("217")
但是不幸的是,失败消息无法告诉您内部期望值
Expecting any elements of:
<["Abcd15", "218"]>
to match given predicate but none did.