Подтвердить что ты не робот

Лучший способ unit test Collection?

Мне просто интересно, как люди unit test и утверждают, что "ожидаемая" коллекция такая же/подобная, что и "фактическая" коллекция (порядок не важен).

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

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

Спасибо.

4b9b3361

Ответ 1

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

ReflectionAssert.assertReflectionEquals(expectedCollection,actualCollection)

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

Ответ 2

Я не уверен, какую версию 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())))
    }
}

Первый тест просто использует matchTo() для каждого объекта (который будет делегировать вашу реализацию равным образом). Если этого недостаточно, вы можете использовать второй случай, который будет использовать геттеры и сеттеры для сравнения каждого элемента. Наконец, вы можете даже написать свои собственные помощники. Пакет Hamcrest не поставляется с матчи для сопоставления по полю (в отличие от сопоставления свойств bean), но тривиально писать FieldMatcher (и, действительно, это хорошее упражнение).

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

Ответ 3

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

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

Ответ 4

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

...