使用assertJ

时间:2017-12-31 12:51:07

标签: junit assertj

我有一个有效的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中的约束不限于子字符串检查)。

5 个答案:

答案 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.