web-dev-qa-db-ja.com

コレクションをユニットテストする最良の方法は?

「期待される」コレクションが「実際の」コレクションと同じ/類似していることを、人々がどのように単体テストして主張するのか疑問に思っています(順序は重要ではありません)。

このアサーションを実行するために、私は単純なアサーション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 Webサイト)であり、そのドメインのすべてのフィールドではありません。もちろん、ビジネスキーだけをチェックするのは理にかなっていますが、ビジネスキーだけでなく、すべてのフィールドが正しいことを確認したい場合があります(たとえば、新しいデータ入力レコード)。したがって、この場合、domain.equals(..)をいじることはできず、collection.containsAll(..)に依存するのではなく、単体テストの目的でいくつかのコンパレーターを実装する必要があるようです。

ここで活用できるテストライブラリはありますか?コレクションをどのようにテストしますか?

ありがとう。

23
limc

Equalsメソッドがすべてのフィールドをチェックしない場合は、Unitils http://unitils.org/ReflectionAssertクラスを使用できます。呼び出し

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

各要素をフィールドごとに反射的に比較します(これはコレクションに適用されるだけでなく、すべてのオブジェクトで機能します)。

8
Jeff Storey

使用しているJUnitのバージョンはわかりませんが、最近のバージョンには、引数として Hamcrest Matcher をとるassertThatメソッドがあります。それらは構成可能であるため、コレクションに関する複雑なアサーションを構築できます。

たとえば、コレクション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実装に委任されます)。それが十分に強力でない場合は、2番目のケースを使用できます。これは、ゲッターとセッターを使用してすべての要素を比較します。最後に、独自のマッチャーを作成することもできます。 Hamcrestパッケージには、(Beanプロパティのマッチングとは対照的に)フィールドごとのマッチング用のマッチャーは付属していませんが、FieldMatcherを作成するのは簡単です(実際、良い演習です)。

マッチャーは最初は少し奇妙ですが、新しいマッチャーを作成する例に従うと、マッチャーを返す静的メソッドを使用して、一連のimport staticsを実行でき、コードは基本的に英語の文のようになります( "assert aは両方ともbにアイテムがあり、b "と同じサイズです)。これらを使用して非常に印象的なDSLを構築し、テストコードをよりエレガントにすることができます。

17
jasonmp85

コレクションを作成していない場合の別のオプション:

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.抑制されている警告を回避するための助けは本当にありがたいです。

1
borjab

jasonmp85の答え の最後の部分をそのまま動作させることができませんでした。一部のjunitjarには、便宜上古いハムクレストのものが含まれているため、使用したインポートを含めました。これは私にとってはうまくいきますが、assertループはhasItems(..)がjasonの答えに書かれているように機能したかのように間違いなく素晴らしいものではありません。

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

...
0
djeikyb