开发者

What unit test to write for a class using generics in Java?

Taking the very specific example of the JpaDao class defined in this article:

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

    @PersistenceContext
    protected EntityM开发者_运维问答anager 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); }
}

would it be best to write unit tests for all the existing entities in the application (Order, Customer, Book, etc.), or would it be acceptable to write unit tests for just one entity, as hinted by this other question? Are there any best practice regarding unit testing java classes using generics?


You could write an abstract test class for entities which subclass this.

Eg:

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
     }
}

The contract you generic classes implement should be able to be tested just as genericlly, assuming that getEntity() sets up and relational dependencies correctly.

Therefore, by subclassing this test class for all the test cases for your generic subclasses, you get the tests for free.


If using a different entity type causes different code to execute, then you need a separate test case.

I'd test as much as I could in a common set of tests that only use one entity type. If most of your code treats all entities the same, then there's no need to test it more than once. I'd set up separate test cases for any special behavior that's required where specific entity DAOs have different behavior.


From the JUnit FAQ:

4) Under what conditions should I test get() and set() methods?

Unit tests are intended to alleviate fear that something might break. If you think a get() or set() method could reasonably break, or has in fact contributed to a defect, then by all means write a test.

In short, test until you're confident. What you choose to test is subjective, based on your experiences and confidence level. Remember to be practical and maximize your testing investment.

It also states:

"Test until fear turns to boredom."

I don't think your question is specific to generics since the question would still be the same even if you weren't using generics. In this case I would choose to test one object (Either real or canned for test purposes). As you discover problems then write tests to address those specific deficiencies.


Like BalusC, I recommend testing concrete implementations. The reason for this is that it conforms to the "You Ain't Gonna Need It" principle. Add just enough tests so that the use case you're trying to implement passes. Then, as you add more use cases, add more unit tests.


If I would need to test the behaviour of the class with regards to its type semantics, I would go for checking type invariants. In other words, try to establish some assertions that are true for all combination of types, not only those which you expect to use but any type in the universe, including those that are not invented yet. For example:

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

See, no matter what types K and E are, the assertions are expected to hold (the testIntegerAndOrder() method tests this assertion using a concrete type values).

This, of course, should be using in conjunction with the unit tests, that are actually testing the behaviour for some particular values of type variables. This would be very much the same unit tests that you could find in any JUnit tutorial. Something in the vein of:

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

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

    dao.persist(order);

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

See how semantics of assertions differ here: this test test an assertion that stored object holds its ID using a concrete value of variable ID, not the type variable as the test before.


Test the concrete class avoid the problem of overriding it later.

It's beautiful to make generic tests but its not safe.

You can CopyPaste most of the Unit Test during the development, and later you can customize the tests.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜