单元测试的最佳方式 集合?

2022-09-01 23:28:05

我只是想知道人们如何进行单元测试并断言“预期”集合与“实际”集合相同/相似(顺序并不重要)。

为了执行此断言,我编写了我的简单断言 API:-

public void assertCollection(Collection<?> expectedCollection, Collection<?> actualCollection) {
    assertNotNull(expectedCollection);
    assertNotNull(actualCollection);
    assertEquals(expectedCollection.size(), actualCollection.size());
    assertTrue(expectedCollection.containsAll(actualCollection));
    assertTrue(actualCollection.containsAll(expectedCollection));
}

好吧,它的工作原理。如果我断言只是一堆整数或字符串,这很简单。例如,如果我试图断言Hibernate域的集合,那也可能非常痛苦。collection.containsAll(..) 依赖于 equals(..) 来执行检查,但我总是覆盖 Hibernate 域中的 equals(..) 以仅检查业务密钥(这是 Hibernate 网站中所述的最佳做法),而不是该域的所有字段。当然,仅针对业务键进行检查是有意义的,但有时我真的想确保所有字段都是正确的,而不仅仅是业务键(例如,新的数据输入记录)。所以,在这种情况下,我不能搞砸 domain.equals(..),而且似乎我需要实现一些比较器来用于单元测试目的,而不是依赖于collection.containsAll(..)。

有没有一些测试库可以在这里利用?您如何测试您的收藏?

谢谢。


答案 1

我不确定你使用的是哪个版本的JUnit,但最近的JUnit有一个方法,它以Hamcrest Matcher作为参数。它们是可组合的,因此您可以建立有关集合的复杂断言。assertThat

例如,如果你想断言一个集合包含集合中的每个元素,你可以这样写:AB

import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;
import static org.hamcrest.core.IsCollectionContaining.*;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import org.hamcrest.beans.SamePropertyValuesAs;

public class CollectionTests {

    /*
    * Tests that a contains every element in b (using the equals()
    * method of each element) and that a has the same size as b.
    */
    @Test
    public void test() {
        Collection<Foo> a = doSomething();
        Collection<Foo> b = expectedAnswer;

        assertThat(a, both(hasItems(b)).and(hasSize(b.size())));
    }

    /*
    * Tests that a contains every element in b (using introspection
    * to compare bean properties) and that a has the same size as b.
    */
    @Test
    public void testBeans() {
        Collection<Foo> a = doSomething();
        Collection<Foo> b = expectedAnswer;
        Collection<Matcher<Foo>> bBeanMatchers =
          new LinkedList<Matcher<Foo>>();

        // create a matcher that checks for the property values of each Foo
        for(Foo foo: B)
            bBeanMatchers.add(new SamePropertyValuesAs(foo));

        assertThat(a, both(hasItems(bBeanMatchers)).and(hasSize(b.size())))
    }
}

第一个测试只是在每个对象上使用 equalTo() 匹配器(这将委托给你的 equals 实现)。如果这还不够强大,则可以使用第二种情况,它将使用 getter 和 setter 来比较每个元素。最后,您甚至可以编写自己的匹配器。Hamcrest 软件包没有附带用于按字段匹配的匹配器(与匹配 bean 属性相反),但是编写 FieldMatcher 是微不足道的(这确实是一个很好的练习)。

Matchers起初有点奇怪,但是如果你按照他们的例子,让新的 Matchers 有一个静态方法返回匹配器,你可以做一堆 s,你的代码基本上读起来像一个英语句子(“断言 a 两者都有 b 中的项,并且大小与 b 相同”)。你可以用这些东西构建一个非常令人印象深刻的DSL,让你的测试代码更加优雅。import static


答案 2

如果 equals 方法未检查所有字段,则可以使用 Unitils http://unitils.org/ 类。叫ReflectionAssert

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

将逐个字段反射地比较每个元素(这不仅仅适用于集合,也适用于任何对象)。


推荐