对我来说,这种测试是一种代码味道。问题始终是:这个测试究竟测试了什么?对于这个测试,你相信什么,你不相信什么?
对我来说,您不能同时相信 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;
}
这对于大多数测试来说绰绰有余。