Лучший способ модульного тестирования коллекции? - PullRequest
23 голосов
/ 27 мая 2010

Мне просто интересно, как люди тестируют модуль и утверждают, что «ожидаемая» коллекция такая же / похожа на «фактическую» коллекцию (порядок не важен).

Чтобы выполнить это утверждение, я написал свой простой 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 (..) полагается на равенства (..), но я всегда переопределяю равенства (..) в моих доменах Hibernate, чтобы проверять только бизнес-ключи (что является наилучшей практикой, указанной в Hibernate) и не все поля этого домена. Конечно, имеет смысл сверяться только с бизнес-ключами, но бывают случаи, когда я действительно хочу убедиться, что все поля верны, а не только бизнес-ключи (например, новая запись ввода данных). Таким образом, в этом случае я не могу возиться с domain.equals (..), и кажется, что мне нужно реализовать некоторые компараторы для целей только модульного тестирования, а не полагаться на collection.containsAll (..).

Есть ли какие-нибудь библиотеки тестирования, которые я мог бы использовать здесь? Как вы тестируете свою коллекцию?

Спасибо.

Ответы [ 4 ]

17 голосов
/ 27 мая 2010

Я не уверен, какую версию 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). Если это недостаточно сильно, вы можете использовать второй случай, который будет использовать геттеры и сеттеры для сравнения каждого элемента. Наконец, вы даже можете написать свои собственные соответствия. В пакете Hamcrest отсутствует средство сопоставления для сопоставления по полю (в отличие от сопоставления свойств бина), но написать FieldMatcher (а это действительно хорошее упражнение) тривиально.

Сначала Matchers немного странно, но если вы последуете их примеру создания новых Matchers, у вас есть статический метод, который возвращает matcher, вы можете сделать кучу import static s, и ваш код в основном читается как английское предложение msgstr "утверждать, что a оба имеют элементы в b и имеют тот же размер, что и b"). С помощью этих вещей вы можете создать довольно впечатляющий DSL и сделать ваш тестовый код более элегантным.

8 голосов
/ 27 мая 2010

Если метод equals не проверяет все поля, вы можете использовать класс Unitils http://unitils.org/ ReflectionAssert. Вызов

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

будет сравнивать каждый элемент отражательно поле за полем (и это относится не только к коллекциям, но и к любому объекту).

1 голос
/ 09 марта 2016

Другой вариант, если вы еще не создали свою коллекцию:

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. Любая помощь, чтобы избежать предупреждения, которое будет подавлено, будет очень признательна.

0 голосов
/ 11 июня 2013

Мне не удалось заставить последнюю часть ответа jasonmp85 работать как есть. Я включил импорт, который использовал, потому что в некоторых банках джунит для удобства есть старые вещи с подколенным сухожилием. Это работает для меня, но цикл assert определенно не так хорош, как если бы 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()));
}

...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...