单元测试你的SQL采取TDD太远了吗?

时间:2009-04-08 15:18:30

标签: sql unit-testing tdd

www.sqlservercentral.com上有关于单元测试SQL的文章。

我的TDD Guy说得好,我们可以测试数据库的东西。

我的系统架构师说,我们测试的逻辑是什么?数据库中不应该有任何逻辑,你应该在数据库中做的唯一事情就是选择,更新或插入。

因此,如果您觉得需要对SQL进行单元测试,那么您是否真的非常彻底,过于务实,还是设计气味的标志?

12 个答案:

答案 0 :(得分:23)

在大多数生活项目中,数据库在项目里程碑之间有一定的流动性。创建,删除或更改表和列。查找表已更新。而且您可以针对数据库的多个实例进行测试,因此最好对数据库中的元数据和数据状态进行一些验证,作为回归测试的一部分。

有几种情况我建议测试数据库:

  • 表& views:验证您希望存在的表和视图。验证这些表和视图是否包含您期望的列。您还可以验证您在此里程碑中删除的表,视图或列实际上是否不存在。

  • 约束:尝试执行应拒绝的数据更改。约束应该阻止这些变化。如果你发现约束不起作用的情况,你可以避免以后的错误。

  • 触发器:与约束相同,触发器也可用于级联效果,或转换值等。测试这些逻辑路径。

  • 存储过程:当在应用程序层中更容易开发,调试和维护逻辑时,我支持在数据库中放入过多逻辑的警告。但有些情况下有令人信服的理由使用存储过程。通常,您会看到通过将复杂的逻辑放入数据库来解决性能瓶颈。所以存储过程不会完全消失,测试它们是一个好主意。

  • 引导数据:查找表是即使在“空”数据库中也需要存在的数据的示例。可以有其他例子。测试数据库是否包含所需数据。

  • 查询:您的应用程序代码包含SQL查询。测试它们的正常功能和性能。特别是性能 - 因为相同的查询有一天可以很好地运行并且在第二天成为瓶颈,因为数据量发生变化,索引变得不平衡等等。

  • ORM类:与触发器类似,应用程序中的ORM类可以包含验证,转换或监视数据库操作的逻辑。这些应该进行测试。

这些测试可能不会被称为“单元测试”。单元测试是一种特定类型的测试,其中每个测试独立于其他测试,并且您尝试单独测试小单元代码。我会说测试数据库,上面列出的方法是功能测试的一个例子。

答案 1 :(得分:12)

您的SQL包含逻辑。例如,布尔条件检查“WHERE”子句。您能想到SQL可能出错的任何方式吗?如果是这样,测试SQL是否有意义,以确保不存在这些错误?

(例如,一些愚蠢的程序员,像我一样,在上面的评论中可能会意外地输入“WHILE”而不是“WHERE”!...就像我做的那样。但我后来纠正了它。那么我的stackoverflow测试在哪里? !?; - )

答案 2 :(得分:10)

我同意系统架构师,现在有太多的业务逻辑正在进入数据库。

答案 3 :(得分:5)

区分单元测试 / specs和集成测试 / specs。

如果您的课程同时存在,那么您违反了合理原则:Separation of Concerns

您应该在单元测试之间明确定义测试,以测试持久性无知的POCO / POJO单元,如实体,服务和集成测试。这些用于测试应用程序击中金属的位置。

集成测试应测试持久性机制(RBDMS),Active Directory,Exchange,文件系统和电子邮件等的持久性,例如存储库和工作单元实现。

如果您的用例需要彻底测试使用触发器的集成点,那么测试行为而不是显式触发器。将来您可以选择不使用触发器并使用ORM或AoP拦截器。

答案 4 :(得分:4)

这个问题引起了热烈的争论。如果您询问DBA,他们会认为这是世界上最好的事情,让您的所有应用程序都使用预定义的存储过程。那些日子即将结束,随着Hibernate和LINQ越来越受欢迎,你真的可以使用数据库作为信息库并让你的数据访问层处理所有请求。我认为除了全文搜索外,LINQ可以在MS SQL中为您完成所有工作。至于SPROC和LINQ之间的性能差异,它可以忽略不计。我的投票是数据库中没有代码,yoru数据访问层中的所有代码,并且已经对它进行了测试。

答案 5 :(得分:3)

这取决于您的数据库架构。如果您只有表和视图,我认为单元测试不是必需的,因为每个(或大多数)错误都会在应用程序中进行单元测试。

但是如果你有复杂的函数,存储过程,触发器等......那么你有很多地方可能是bug而且应用程序单元测试没有覆盖它们。

答案 6 :(得分:2)

系统架构师是正确的。不应该在您的数据库中插入业务逻辑,因此您实际上并不是单元测试任何东西。

答案 7 :(得分:1)

我认为这有点矫枉过正。我想你可以做到并将测试放在一个特殊的类别中,这样它们就不会在每次构建时运行,也许只是在签入并在服务器上运行时。通常,对于所有单元测试,您不需要外部依赖项。

答案 8 :(得分:1)

我不直接在我的数据库上执行TDD,但是有很多机会将“逻辑”放入数据库是有效的。约束,默认值(是的,我知道它也是一个约束),触发器等。通常这些是实现某些业务逻辑和确保数据库一致性的最佳方式。大部分时间我都能通过一些手动测试来说服自己的正确性,并将其留在那里,但我可以看到有人可能想用TDD做这个。

修改

例如,我将使用插入时的默认值和更新时的触发器来设置插入/更新时的“上次更新时间”字段。在LINQ中,我将列设置为自动生成的值并使其为只读。对我来说,这比添加PropertyChanged事件处理程序更简单,以确保每当实体上的字段发生更改时,上次更新的时间也会更改。我测试一下吗?当然,但是手动和事后,尽管我非常喜欢TDD用于大多数事情。

答案 9 :(得分:1)

我不知道为什么当我们点击db层时,所有好的练习都应该进入窗口。如果这一层中存在逻辑,那么它的双重重要性。有一个很好的工具构建在Fitnesse之上,称为dbfit,似乎可以解决对dblayer进行单元测试的所有麻烦。如果你有兴趣,你应该看看。

答案 10 :(得分:0)

只要WHERE子句不为空,就应该测试它。

这里我们使用NHibernate Criteria API来查询数据库。尽管如此,我们还是进行了简单的单元测试来保护数据访问层。考虑一下:

public IList<Book> GetBorrowedBooks(User user);

首先看起来可能很傻。但是对于这样一个简单的情况,我们正在处理至少3个模型对象:Book,User,Borrow和Return。任何修改3个(或更多)类中任何一个的尝试都可能会破坏代码。

费用是多少?我想,在这个例子中编写测试的时间不到20分钟。借助NUnit中的Category,数据访问单元测试可以配置为在晚上运行,而其他测试则在每次提交时运行。缓慢的数据访问单元测试不会造成伤害,并且它们可以节省生命。

答案 11 :(得分:0)

DbFit是用于单元测试数据库的好工具。我认为将TDD与SQL一起使用是明智的,因为毕竟它是一种带有条件分支,聚合函数等的声明性语言。如果没有测试,你怎么能确定你得到了预期的结果呢?