通常会发现自己编写针对数据库调用的单元测试,并且我总是遇到同样的问题:如何验证是否将好的查询发送到数据库?
示例,我有这个类将以下列形式向数据库发送最终更新:
update credential set password_hash = ?, password_crypt = ?, password_plain = ? where id = ?
(这是一个密码迁移工具,请不要介意password_plain
字段的安全问题)
为这个类编写测试类,我已经模拟了数据库访问类(在这种情况下我使用的是Spring JDBCTemplate)并捕获了已发布的sql。在我有了sql之后,我做了以下检查:
String space = "\\s+";
String optSpace = "\\s*";
String something = ".+";
String optSomething = ".*";
sql = sql.toLowerCase();
assertTrue(sql.matches(optSpace + "update" + space + "credential" + space + "set" + space + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_hash" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_crypt" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "set" + space + optSomething + "password_plain" + optSpace + "=" + optSpace + "\\?" + something + "where" + something));
assertTrue(sql.matches(something + space + "where" + space + optSomething + "id" + optSpace + "=" + optSpace + "\\?" + optSomething));
通过这些检查,我确实验证了发布的SQL是否包含更新中最重要的部分,如:
id
语句中使用where
,其值为参数我可以简单地验证发出的查询是否完全是上面预期的查询,但这会使测试对未来的更改过于限制,并且如果查询的任何部分发生更改,即使更新保持正确,也会强制失败。我认为测试的编写主要是为了将来使用(当你更换软件并需要更多的保证时)而不是现在,这个选项会使测试变得毫无用处。
好吧,最后,我宣布了一个问题:我们有哪些更好的选项来验证发布的SQL?
我看到许多项目创建了小型嵌入式数据库,其中包含少量数据,用于测试处理数据库的类,但我想编写更多纯单元测试替代方案(如果我可以称之为)
答案 0 :(得分:2)
我不认为对真实数据库进行测试是不错的选择(即使它是嵌入式等)。目前您正在测试您的SQL在语法上是否有效,但它实际上是否正常工作。例如你知道它是否会违反限制等......
Mocking等一切都很好但是在某个阶段你必须对数据库进行测试。我会确保在可能的情况下,您不要对数据库进行测试,然后咬住子弹并围绕一个小型数据库(具有适当的回滚/重建等)构建测试,以实际确认正确的数据库功能。
答案 1 :(得分:1)
我恳请您重新考虑这些测试的好处,因为
对于最终的DataAccessLayer,我建议编写集成测试。一个针对真实但最小的DB运行的。当然这些测试会很慢,但他们提供的信心是值得的。
因此,针对GetCustomers()编写测试,并验证返回的DTO是否包含正确的数据,并验证您发出的SQL查询是否为X.
答案 2 :(得分:1)
不要断言你的sql。这是毫无意义。您最终会将传递的sql字符串与另一个字符串进行比较(也是由您创建的,因此没有验证)或者您必须实现自己的数据库。而只是使用现有的。 check is query返回正确的数据或正确更改数据库中的数据。使用dbunit或类似的。