如何使用单元测试和TDD来测试主要依赖数据库CRUD操作的应用程序?


22

在工作中,我的项目之一主要是获取从外部客户端传入的数据并将其保存在数据库中。这是一个使用JPA的Java企业应用程序,我们的大多数逻辑都围绕CRUD操作展开。

我们的大多数错误都以一种或另一种方式涉及JPA。

  • 示例1:如果单击保存按钮两次,JPA可能会尝试第二次将同一实体插入数据库,从而导致主键冲突。
  • 示例2:您从数据库中检索一个实体,对其进行编辑,然后尝试更新其数据。JPA可能会尝试创建一个新实例,而不是更新旧实例。

解决方案通常需要添加/删除/更改JPA批注。其他时候,它与修改DAO逻辑有关。

我无法弄清楚如何使用单元测试和TDD对我们的代码充满信心。我不确定这是因为单元测试和TDD不合适,还是我错误地解决了这个问题。

单元测试似乎不合适,因为我只能在运行时发现这些问题,并且需要部署到应用服务器以重现这些问题。通常需要涉及数据库,而我认为这超出了单元测试的定义:这些是集成测试。

TDD似乎不合适,因为部署+测试反馈循环是如此之慢,以至于我效率低下。部署+测试反馈循环需要3分钟以上的时间,这就像我专门针对所编写的代码运行测试一样。要运行所有集成测试,需要30分钟以上的时间。

在此模型之外有代码,我总是尽可能地进行单元测试。但是,我们的大多数错误和最大的时间浪费总是涉及JPA或数据库。


还有另一个类似的问题,但是如果我遵循建议,我将包装代码中最不稳定的部分(JPA)并测试除此以外的所有内容。就我的问题而言,我将处于同样的糟糕境地。打包JPA之后的下一步是什么?海事组织,这个问题(也许)是回答我的问题的步骤,而不是答案。


4
您要做的实际上是集成测试,因为您必须设置数据库才能进行实际测试。我可以想象一个模块将依赖于其他模块,因此使其更像集成测试。我将改变您对如何将TDD方法应用于您的应用程序的问题。
知情的2014年

@random:是的,我编辑了问题以明确地说出来。我不明白您为什么建议我更改问题。你能详细说明吗?我想保留单元测试部分,因为我宁愿编写单元测试,也不愿进行集成测试(尽管我知道unit testing != TDD
Daniel Kaplan 2014年

没什么特别的,只是把TDD放在那。如果您有单元测试有,那么很多人会认为你不明白的东西,等等。对你也不好..
InformedA


Answers:


7

一种选择是使用内存中的测试数据库,例如H2。它通常比使用磁盘的标准数据库快约10倍,并且启动/删除时间更短。

是否会有所帮助在很大程度上取决于您遇到的JPA问题是否足够普遍,以至于它们在其他数据库上仍然会失败。如果它们遗漏了大部分问题,则不会有很多点运行测试会更快。

但是,如果整个系统每运行一次H2都能进行10次运行,那么它可能会有所回报。


这是一个很好的想法,但是我仍然必须部署到应用服务器AFAIK。这是3分钟以上的很多时间。也就是说,这绝对是值得做的。但是仍然很难想象像我运行单元测试那样频繁地运行测试,因此使用TDD进行开发似乎效率低下。
Daniel Kaplan 2014年

1
我认为通常可以通过多种方法来解决该要求(例如docs.oracle.com/middleware/1212/toplink/TLADG/testingjpa.htm)。不过,有更多的工作要做而不是合理的。另一个选择是获得一些更强大的测试服务器并并行运行。
soru 2014年

1
@tieTYT我自己的hsqldb单元概念证明在github上测试了一个Crud Web应用程序:TestingWithHsqldb-单元测试不需要部署该应用程序。

3

数据库可以非常容易地进行单元测试-您需要存储过程和事务。

这就是微软对数据库单元测试的看法。您还可以针对数据库运行单元测试,通过建立数据库连接,用Java或C#编写测试,开始事务,将要用于测试的任何数据写入数据库,运行测试然后回滚。如果您使用的数据库也已部署到数据库中,并且得到了完全隔离的测试,则不会损坏数据库。

希望这可以给您一些见识,如何在您的框架内做到这一点。


正如我说的:“我们的大多数错误都以某种方式涉及JPA。”我认为本文的建议会遗漏所有这些。另外,如果您认为那些Java / C#测试仍然是单元测试,则我们有非常不同的定义。我认为总体而言,这是个不错的建议,但似乎仍然需要花费大量时间来部署和运行套件,因此不利于TDD。你不同意吗?
Daniel Kaplan 2014年

我们曾经为SQL运行数据库单元测试,但是后来它们都在存储过程中了。虽然你可以从其他SQL过程的单元测试SQL目录,我们的单元测试框架是MSTest的所以它是有道理的,从运行它们那里(嘿嘿,我们得到了这是构建服务器绿色刻度最重要的因素)。如果您有一个始终处于运行状态的数据库(无论如何我们还是进行了内部测试),那么很容易上载所有sql代码并在构建服务器上运行所有单元测试。有时您只需要对这些事情保持务实。
gbjbaanb'4

我认为您没有回应我的第一句话。
Daniel Kaplan 2014年

好吧,那就用jpa-unit吧。我无法回答您的JPA代码如何工作(或不起作用),只是尝试给您一些有关在db中测试该sql的想法。
gbjbaanb 2014年

3

其他人回答“模拟数据库!” -但是,如果您实际上需要测试数据库层与代码的交互方式,那么模拟数据库层又有什么意义呢?

您正在寻找的是集成测试和/或自动UI测试。您提到在以下情况下会发生此问题:

*If you click the save button twice*

对此进行测试的唯一方法是编写一个自动UI测试,以两次单击按钮。也许看看硒。

您可能还需要一个单元测试数据库,并且您的测试将其指向该单元。难以维持,但欢迎来到现实世界中的TDD。


这读起来更像是一个夸夸其谈不是答案
蚊蚋

我已经回答了3次问题-集成测试,GUI测试和/或单元测试DB。是的,这有点麻烦,我现在将其编辑为某种理智的外观。
罗克兰

1
“对此进行测试的唯一方法是编写一个自动UI测试,两次单击按钮两次。也许可以查看Selenium。” 在这种情况下,后端最好防止这种情况的发生,否则UI将可以直接访问数据库。
丹尼尔·卡普兰

0

在您提出问题的示例中,您无法对单击两次按钮以很容易导致错误的情况进行单元测试/ TDD。但是您可以进行单元测试的是,在单击按钮时调用的代码中,如果您从持久层收到异常,则可以适当地对其进行处理(通过模拟持久层或使用内存数据库作为已在其他答案中提出)-通过重新抛出或显示错误或其他任何方式。

没错,当您需要执行不太适合单元测试的测试(即集成/系统测试)时,TDD可能会开始崩溃-这在最近的“是TDD吗?死?” 肯特·贝克(Kent Beck),马丁·福勒(Martin Fowler)和戴维·海涅迈尔·汉森(David Heinemeier Hansson)之间的辩论:http//martinfowler.com/articles/is-tdd-dead/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.