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

时间:2010-05-27 16:22:01

标签: java unit-testing testing junit

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

为了执行这个断言,我写了一个简单的断言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(..)。 / p>

我可以在这里使用一些测试库吗?你如何测试你的收藏?

感谢。

4 个答案:

答案 0 :(得分:16)

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

例如,如果你想断言集合A包含集合B中的每个元素,你可以写:

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有一个返回匹配器的静态方法你可以做一堆import static s你的代码基本上就像英文一样句子(“断言两者都有b中的项目并且与b具有相同的大小”)。你可以用这些东西建立一个非常令人印象深刻的DSL,让你的测试代码更加优雅。

答案 1 :(得分:8)

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

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

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

答案 2 :(得分:1)

如果您尚未构建集合,则为另一种选择:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.Matchers.is;

@Test
@SuppressWarnings("unchecked")
public void test_returnsList(){

    arrange();

    List<MyBean> myList = act();

    assertThat(myList , contains(allOf(hasProperty("id",          is(7L)), 
                                       hasProperty("name",        is("testName1")),
                                       hasProperty("description", is("testDesc1"))),
                                 allOf(hasProperty("id",          is(11L)), 
                                       hasProperty("name",        is("testName2")),
                                       hasProperty("description", is("testDesc2")))));
}

如果您不想检查对象的顺序,请使用containsInAnyOrder

P.S。任何帮助以避免被禁止的警告将非常感激。

答案 3 :(得分:0)

我无法让jasonmp85's answer的最后一部分按原样运行。我包括了我使用的进口,因为为方便起见,一些junit罐包括旧的hamcrest东西。这对我有用,但断言循环肯定不如jason的答案中所写的那样好hasItems(..)

import org.hamcrest.Matcher;
import org.hamcrest.beans.SamePropertyValuesAs;
import org.hamcrest.collection.IsCollectionWithSize;

import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;

...

/*
* 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));

    // check that each matcher matches something in the list
    for (Matcher<Foo> mf : bBeanMatchers)
        assertThat(a, hasItem(mf));

    // check that list sizes match
    assertThat(a, IsCollectionWithSize.hasSize(b.size()));
}

...