在JUnit中解决重复的测试习惯用法

时间:2011-11-02 22:30:01

标签: java unit-testing coding-style junit xunit

我有2个案例对我来说似乎是同样的问题,即使它们情况完全不同:

1)我正在测试对数据库的读写。因为我每次都在清理和重建对象,所以写入测试需要读取以确认每个字段的写入,并且读取测试首先写入,因此测试最终看起来相同。然而,我不想在未经测试的界面中留下一个主要方法。

2)在一个小得多的情况下,我正在测试一个小数据对象的copy()方法和equals()方法。 copy()方法使用equals()来测试自身,而equals()方法正在测试副本。同样,测试也是重复的。

我觉得我在这里遗漏了一些东西,某种方式来分离依赖关系而不创建大量的额外工作(比如将原始JDBC写入数据库等等)是否有一种标准的方法来处理这种情况测试重复?

2 个答案:

答案 0 :(得分:1)

作为一个肤浅的测试,你正在做的事情很好。毕竟,你想要做的是断言write方法和read方法是互补的,当你编写和读取时,你获得了一个相等的对象(同样适用于copy和equals) 不幸的是,我不认为你可以在没有额外工作的情况下更深入,正如你已经知道的那样。测试应该非常简单,不需要额外的测试,除非你编写第二个写入和读取实现,否则你必须进行手工操作。

答案 1 :(得分:1)

对我来说,这种测试是代码味道。问题始终是:完全这个测试测试是什么?对于这个测试,你信任什么,不信任什么?

对我来说,你不能信任read()和write(),它们可能属于同一个人,由同一个人写的。因此,如果你通过调用write()测试r​​ead(),那么这不是一个好的测试,你测试的是write()和read()是同步的,而不是他们做他们应该做的事情。

在第二个示例中,您正在测试该副本和等号同步,同样的问题。

让我们说这是持久层的实现:

public class PersistenceLayer {
    private Object object;

    void write(Object object) {
        this.object = object;
    }

    Object read(Long id) {
        return object;
    }
}

问题是,您的测试是否会通过此持久层传递?但它显然不能做你想要的。它不靠近数据库。同样地,如果你的阅读&写共享会话/事务?在这种情况下,数据可能永远不会实际提交到数据库。它可能会在最后进行回滚。但是你的测试仍然会通过。

阅读你的描述,你正在测试当我调用write()然后read()时,我得到一个类似的对象。我对write()方法的期望是它将数据写入数据库。所以,如果我正在测试那个,我需要检查一下。所以我必须有另一个通道,我可以用来测试读取和放大器写。这通常最终会通过JDBC创建一个新的Connection并执行select。

所以我的测试代码是

testWrite() {
    write(o);
    Object o2 = readByJdbc("SELECT * FROM table WHERE id = ?", o);
    assertObjectsEqual(o, o2); // this needs to compare all values
}

testRead() {
    write(o);
    Object o2 = read(o.id);
    Object o3 = readByJdbc("SELECT * FROM table WHERE id = ?", o);

    assertObjectsEqual(o2, o3); // this needs to compare all values
}

testWrite()写入数据库并通过打开JDBC连接并以此方式读取(不同的会话,不同的事务,即数据将在数据库中)来确保将数据写入数据库。

testRead()写入数据库,并通过持久层和jdbc比较读取返回的两个对象。我正在复制写入(o)的调用,但它是可以接受的,因为我们知道在调用其他测试时write是否有效。我可以写另一个writeByJdbc,但我得到的只是一个测试会失败而不是两个。

实际上,根据您的偏执程度,您不需要比较assertObjectsEqual()中的所有值。例如,如果您正在使用hibernate,您可以假设所有内容都已正确声明,并测试数据库中是否存在行。我经常这样做,因为我相信休眠。但在这种情况下,我需要测试我如何调用hibernate,如何定义对象。

jdbc代码不需要冗长而复杂,对于简单的选择,我只需要创建列的值列表列表:

private List<Map<String, Object>> resultSetToListMap(ResultSet resultSet) throws SQLException {
    int columnCount = resultSet.getMetaData().getColumnCount();
    List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();

    while (resultSet.next()) {
        Map<String, Object> map = new LinkedHashMap<String, Object>();

        for (int i = 1; i <= columnCount; i++) {
            map.put(resultSet.getMetaData().getColumnName(i), resultSet.getObject(i));
        }

        list.add(map);
    }

    return list;
}

这对于大多数测试来说已经足够了。