2

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

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

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

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

4

2 回答 2

1

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

于 2011-11-02T23:07:05.473 回答
1

对我来说,这种测试是一种代码味道。问题始终是:这个测试究竟测试了什么?对于这个测试,你相信什么,你不相信什么?

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

在第二个示例中,您正在测试 copy 和 equals 是否同步,同样的问题。

可以说这是持久层的实现:

public class PersistenceLayer {
    private Object object;

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

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

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

阅读您的描述,您正在测试当我调用 write() 然后 read() 时,我得到了一个类似的对象。我对 write() 方法的期望是它将数据写入数据库。因此,如果我正在测试,我需要检查它。所以我必须有另一个通道来测试读写。这通常最终通过 JDBC 创建一个新的连接并进行选择。

所以我的测试代码是

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 读取返回的两个对象。我正在复制对 write(o) 的调用,但这是可以接受的,因为我们知道当另一个测试被调用时 write 是否会起作用。我可以写另一个 writeByJdbc,但我得到的只是一个测试会失败而不是两个。

事实上,根据你的偏执程度,你不需要比较 assertObjectsEqual() 中的所有值。例如,如果您使用休眠模式,您可以假设所有内容都已正确声明,并测试数据库中是否存在该行。我经常这样做,因为我相信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;
}

这对于大多数测试来说绰绰有余。

于 2011-11-03T08:22:36.287 回答