我如何断言一个List恰好包含一个特定类的一个实例?

rds*_*rds 2 java hamcrest google-truth

我想测试一个列表包含一个对象的实例。

例如,对于一个实例:

assertThat(mylist).containsExactly(Matchers.any(ExpectedType.class));
Run Code Online (Sandbox Code Playgroud)

从test返回的数组obj确实包含instance的一个对象ExpectedType。但是我的测试失败了:

java.lang.AssertionError:<[ExpectedType @ 7c781c42]>完全包含<[ExpectedType的实例]>是不正确的。它缺少<[ExpectedType的实例]>,并且具有意外的项目<[ExpectedType @ 7c781c42]>

如何编写此测试?

hee*_*nee 5

您正在尝试编写一个测试,以List使用Hamcrest Truth 来查看一个类是否恰好包含一个特定类的实例。相反,您应该使用 Hamcrest Truth 编写此测试。Hamcrest和Truth都是用于使测试更具表现力的库,每个库都有自己的特定用法,样式和语法。如果愿意,可以在测试中将它们并排使用,但是在执行过程中将它们的方法链接在一起是行不通的。(也许您会感到困惑,因为这两个库都可以具有以assertThat?。)因此,对于此特定测试,您需要选择其中一个并进行处理。

但是,这两个库都缺少检查a List是否具有一个且只有一个的内置功能。满足条件的。因此,对于任何一个库,您都有两个选择:要么可以对列表进行一些预处理,以便可以使用内置的断言,要么可以扩展库的语言以提供此功能。

下面是一个示例类,演示了两个库的两个选项:

import com.google.common.collect.FluentIterable;
import com.google.common.truth.*;
import org.hamcrest.*;
import org.junit.Test;

import java.util.*;

import static com.google.common.truth.Truth.assertAbout;
import static com.google.common.truth.Truth.assert_;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;

public class ExactlyOneInstanceTest {
    List<Object> myList = Arrays.asList("", 3, 'A', new Object());

    @Test
    public void hamcrestBuiltInTestExactlyOneInstance() {
        long theNumberOfStringsInMyList = myList.stream().filter(o -> o instanceof String).count();
        assertThat(theNumberOfStringsInMyList, equalTo(1L));
    }

    @Test
    public void hamcrestExtendedTestExactlyOneInstance() {
        assertThat(myList, HasExactlyOne.itemThat(is(instanceOf(String.class))));
    }

    @Test
    public void truthBuiltInTestExactlyOneInstance() {
        long theNumberOfStringsInMyList = myList.stream().filter(o -> o instanceof String).count();
        // can't static import Truth.assertThat because of name clash,
        // but we can use this alternative form
        assert_().that(theNumberOfStringsInMyList).isEqualTo(1);
    }

    @Test
    public void truthExtendedTestExactlyOneInstance() {
        assertAbout(iterable()).that(myList).containsExactlyOneInstanceOf(String.class);
    }


    // Hamcrest custom matcher
    static class HasExactlyOne<T> extends TypeSafeDiagnosingMatcher<Iterable<? super T>> {
        Matcher<? super T> elementMatcher;

        HasExactlyOne(Matcher<? super T> elementMatcher) {
            this.elementMatcher = elementMatcher;
        }

        @Factory
        public static <T> Matcher<Iterable<? super T>> itemThat(Matcher<? super T> itemMatcher) {
            return new HasExactlyOne<>(itemMatcher);
        }

        @Override
        public void describeTo(Description description) {
            description
                .appendText("a collection containing exactly one item that ")
                .appendDescriptionOf(elementMatcher);
        }

        @Override
        protected boolean matchesSafely(Iterable<? super T> item, Description mismatchDescription) {
            return FluentIterable.from(item).filter(o -> elementMatcher.matches(o)).size() == 1;
        }
    }

    // Truth custom extension
    static <T> SubjectFactory<ExtendedIterableSubject<T>, Iterable<T>> iterable() {
        return new SubjectFactory<ExtendedIterableSubject<T>, Iterable<T>>() {
            @Override
            public ExtendedIterableSubject<T> getSubject(FailureStrategy fs, Iterable<T> target) {
                return new ExtendedIterableSubject<>(fs, target);
            }
        };
    }

    static class ExtendedIterableSubject<T> extends IterableSubject<ExtendedIterableSubject<T>, T, Iterable<T>> {
        ExtendedIterableSubject(FailureStrategy failureStrategy, Iterable<T> list) {
            super(failureStrategy, list);
        }

        void containsExactlyOneInstanceOf(Class<?> clazz) {
            if (FluentIterable.from(getSubject()).filter(clazz).size() != 1) {
                fail("contains exactly one instance of", clazz.getName());
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

尝试运行并查看该类,并使用最适合您的方式。在编写将来的测试时,只需尝试使用您可以使用的内置断言,并尝试使@Test方法及其断言的意图立即可读。如果看到您多次编写相同的代码,或者测试方法不是那么容易阅读,则可以重构和/或扩展所用库的语言。重复进行,直到所有内容都经过测试并且所有测试都易于理解为止。请享用!