Какой модульный тест написать для класса с использованием обобщений в Java? - PullRequest
14 голосов
/ 19 января 2011

Взяв очень конкретный пример класса JpaDao, определенного в этой статье :

public abstract class JpaDao<K, E> implements Dao<K, E> {
    protected Class<E> entityClass;

    @PersistenceContext
    protected EntityManager entityManager;

    public JpaDao() {
        ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
        this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[1];
    }

    public void persist(E entity) { entityManager.persist(entity); }

    public void remove(E entity) { entityManager.remove(entity); }

    public E findById(K id) { return entityManager.find(entityClass, id); }
}

, было бы лучше написать модульные тесты для всех существующих сущностей в приложении(Order, Customer, Книга и т. Д.), Или было бы приемлемо написать модульные тесты только для одной сущности, на что намекал этот другой вопрос ?Есть ли лучшая практика в отношении модульного тестирования Java-классов с использованием дженериков?

Ответы [ 6 ]

6 голосов
/ 19 января 2011

Вы можете написать абстрактный тестовый класс для сущностей, которые подкласс этого.

Например:

public abstract class JpaDaoTest<K,E> {

    abstract protected E getEntity();
    abstract protected JpaDao getDAO();

    @Test
    public void testPersistCreatesEntity()
    {
         JpaDao dao = getDAO();
         dao.persist(getEntity());
         // assert
     }
}

Контракт, реализуемый вашими родовыми классами, должен быть в состоянии тестироваться так же, как и в общем, при условии, что getEntity() правильно устанавливает и реляционные зависимости.

Таким образом, подклассы этого класса тестов для всех тестовых случаев для ваших общих подклассов позволяют получить тесты бесплатно.

1 голос
/ 19 января 2011

Как и BalusC, я рекомендую тестировать конкретные реализации. Причина этого заключается в том, что он соответствует принципу «Вам это не нужно». Добавьте достаточно тестов, чтобы вариант использования, который вы пытаетесь реализовать, прошел. Затем, когда вы добавляете больше вариантов использования, добавляйте больше юнит-тестов.

1 голос
/ 19 января 2011

Из FAQ по JUnit:

4) При каких условиях я должен тестировать методы get () и set ()?

Модульные тесты предназначены для снятия страха, что что-то может сломаться.Если вы считаете, что методы get () или set () могут быть разумно повреждены или действительно привели к дефекту, то непременно напишите тест.

Короче, тестируйте, пока не будете уверены.То, что вы выбираете для тестирования, субъективно, основано на вашем опыте и уровне уверенности.Не забывайте практиковать и максимально увеличить свои инвестиции в тестирование.

В нем также говорится:

«Тестируйте, пока страх не превратится в скуку».

Я не думаю, что ваш вопрос относится к дженерикам, так как вопрос все еще будет таким же, даже если вы не использовали дженерики.В этом случае я бы выбрал для тестирования один объект (реальный или консервированный для целей тестирования).Когда вы обнаружите проблемы, напишите тесты для устранения этих конкретных недостатков.

1 голос
/ 19 января 2011

Если использование другого типа сущности приводит к выполнению другого кода, то вам нужен отдельный контрольный пример.

Я бы проверял столько, сколько мог, в общем наборе тестов, которые используют только один тип сущности,Если большая часть вашего кода обрабатывает все сущности одинаково, то нет необходимости тестировать его более одного раза.Я бы настроил отдельные тестовые случаи для любого особого поведения, которое требуется, когда определенные DAO сущности ведут себя по-разному.

0 голосов
/ 07 августа 2013

Проверьте конкретный класс, чтобы избежать проблемы переопределения его позже.

Прекрасно делать общие тесты, но это небезопасно.

Вы можете CopyPaste большую часть модульного теста во время разработки, а позже вы можете настроить тесты.

0 голосов
/ 19 января 2011

Если бы мне нужно было проверить поведение класса в отношении его семантики типов, я бы пошел для проверки инвариантов типов. Другими словами, попытайтесь установить некоторые утверждения, которые верны для всех комбинаций типов, не только тех, которые вы ожидаете использовать, но и любого типа во вселенной, включая те, которые еще не изобретены. Например:

private <K, E> void testTypes(K k, E e) {
    JpaDao<K, E> dao = new JpaDaoImpl<K, E>();

    dao.persist(e);

    assertEquals(dao.getById(e.getId()).getClass(), e.getClass());
}

@Test
public void testIntegerAndOrder() {
    this.<Integer, Order>testTypes(10, new Order());
}

Понимаете, независимо от того, какие типы K и E, предположения должны сохраняться (метод testIntegerAndOrder() проверяет это утверждение с использованием значений конкретного типа).

Это, конечно, следует использовать в сочетании с модульными тестами, которые фактически проверяют поведение для некоторых конкретных значений переменных типа. Это были бы те же самые модульные тесты, которые вы можете найти в любом руководстве по JUnit. Что-то в духе:

@Test
public void testDao() throws Exception {
    JpaDao<Integer, Order> dao = getDao();

    Order order = ...;
    order.setId(10);

    dao.persist(order);

    assertEquals(order, dao.findById(10));
}

Посмотрите, как отличается семантика утверждений: в этом тесте проверяется утверждение, что хранимый объект хранит свой идентификатор, используя конкретное значение идентификатора переменной, а не переменную типа, как в предыдущем тесте.

...