Java 单元测试集合的最佳方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2922879/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 14:32:40  来源:igfitidea点击:

Best way to unit test Collection?

javaunit-testingtestingjunit

提问by limc

I'm just wondering how folks unit test and assert that the "expected" collection is the same/similar as the "actual" collection (order is not important).

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

To perform this assertion, I wrote my simple assert API:-

为了执行此断言,我编写了简单的断言 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));
}

Well, it works. It's pretty simple if I'm asserting just bunch of Integers or Strings. It can also be pretty painful if I'm trying to assert a collection of Hibernate domains, say for example. The collection.containsAll(..) relies on the equals(..) to perform the check, but I always override the equals(..) in my Hibernate domains to check only the business keys (which is the best practice stated in the Hibernate website) and not all the fields of that domain. Sure, it makes sense to check just against the business keys, but there are times I really want to make sure all the fields are correct, not just the business keys (for example, new data entry record). So, in this case, I can't mess around with the domain.equals(..) and it almost seems like I need to implement some comparators for just unit testing purposes instead of relying on collection.containsAll(..).

嗯,它有效。如果我只是断言一堆整数或字符串,那就很简单了。例如,如果我试图断言 Hibernate 域的集合,这也可能非常痛苦。collection.containsAll(..) 依赖于 equals(..) 来执行检查,但我总是覆盖我的 Hibernate 域中的 equals(..) 以仅检查业务键(这是Hibernate 网站)而不是该域的所有字段。当然,只检查业务键是有意义的,但有时我真的想确保所有字段都正确,而不仅仅是业务键(例如,新数据输入记录)。所以,在这种情况下,我不能乱用 domain.equals(..

Are there some testing libraries I could leverage here? How do you test your collection?

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

Thanks.

谢谢。

采纳答案by Jeff Storey

If the equals method doesn't check all the fields, you can use the Unitils http://unitils.org/ReflectionAssertclass. Calling

如果equals 方法没有检查所有字段,您可以使用Unitils http://unitils.org/ReflectionAssert类。打电话

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

will compare each element reflectively field by field (and this doesn't just apply for collections, it will work for any object).

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

回答by jasonmp85

I'm not sure what version of JUnit you're using, but recent ones have an assertThatmethod which takes a Hamcrest Matcheras an argument. They're composable so you can build up complex assertions about a collection.

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

For instance, if you wanted to assert that a collection Acontained every element in collection B, you could write:

例如,如果您想断言一个集合A包含collection中的每个元素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())))
    }
}

The first test just uses the equalTo() matcher on every object (which will delegate to your equals implementation). If that's not strong enough, you can use the second case, which will use getters and setters to compare every element. Finally, you can even write your own matchers. The Hamcrest package doesn't come with a matcher for matching by field (as opposed to matching bean properties), but it's trivial to write a FieldMatcher (and indeed is a good exercise).

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

The Matchers are a bit odd at first, but if you follow their example of making new Matchers have a static method that returns the matcher you can do a bunch of import statics and your code basically reads like an English sentence ("assert that a both has the items in b and has the same size as b"). You can build up a pretty impressive DSL with these things and make your test code a lot more elegant.

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

回答by djeikyb

I couldn't get the last part of jasonmp85's answerto work as is. I included the imports I used because some junit jars include old hamcrest stuff for convenience. This works for me, but the assert loop definitely isn't as nice as if hasItems(..)worked as written in jason's answer.

我无法按原样得到jasonmp85 答案的最后一部分。我包括了我使用的进口,因为一些junit jars为了方便起见包括旧的hamcrest东西。这对我有用,但断言循环绝对不像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()));
}

...

回答by borjab

Another option if you have not build your collection:

如果你还没有建立你的收藏,另一种选择:

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")))));
}

Use containsInAnyOrderif you do not want to check the order of the objects.

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

P.S. Any help to avoid the warning that is suppresed will be really appreciated.

PS 任何避免被禁止的警告的帮助将不胜感激。