在应用程序的CRUD层上创建单元测试,如何使测试独立?


14

因此,我试图使我的单元测试尽可能地按书进行,但是当我测试一些简单的添加/删除方法时,这会很麻烦。

对于add方法,我基本上必须创建一个虚拟对象并将其添加,然后在测试成功之后,我必须删除该虚拟对象。

对于删除测试,我显然必须创建一个虚拟对象,以便可以将其删除。

如您所见,如果一项测试失败,那么另一项测试也会失败,因为它们都是必需的。

与需要编写“取消订单”测试的系统相同……那么首先需要取消一些虚拟订单,这是否违反单元测试的准则?

这样的案件应该如何处理?


你可能也想看看这个问题:programmers.stackexchange.com/questions/115455/...
居文

Answers:


13

好吧,您所做的没有错。多个测试可以覆盖相同的代码;这仅意味着一个问题将导致多个测试失败。您要避免的测试取决于其他测试的结果。即,删除测试取决于已运行的添加测试,因此,如果在添加测试之前运行删除测试,它将失败。为避免该问题,请确保在每个测试开始时都有一个“空白”,这样在给定测试中发生的情况不会影响后续测试。

做到这一点的一种好方法是针对内存数据库运行测试。

在添加测试中,创建一个空数据库,添加对象,并断言它确实已添加。

在删除测试中,使用要删除的对象创建数据库。删除对象,并断言它已被删除。

用您的拆卸代码删除数据库。


内存数据库只是快速的(在内存中)和简单的(在过程中)。您可以对任何数据存储执行此操作。
Paul Draper 2015年

3

使用交易。

如果使用的数据库支持事务,则在事务中运行每个测试。在测试结束时回滚事务。然后,每个测试将使数据库保持不变。

是的,要取消订单,您首先必须创建一个订单。没关系。测试将首先创建一个订单,然后将其取消,然后确认该订单已被取消。


喜欢这个主意。今天实施了很大的效果。
pimbrouwers

3

你做的很好。单元测试的唯一且唯一的基本原则是覆盖您拥有的每个代码路径,因此您可以确信您的代码正在执行应做的事情,并在更改和重构后继续执行。使单元测试保持小巧,简单和单一用途是一个值得的目标,但这不是根本。进行调用两个API相关方法的测试本身并不值得怀疑,实际上,正如您所指出的那样,这通常是必要的。拥有冗余测试的缺点在于,它们需要花费更多的时间来编写代码,但是由于几乎所有开发中的内容,这都是您必须始终付出的一种折衷,而最佳的解决方案几乎从来都不是极端问题之一。


2

软件测试技术千差万别,您对它们的了解越多,您将开始看到很多不同的(有时是相互矛盾的)指南。没有一本“书”。

我认为您处于一种已经看到一些有关单元测试的指导的情况,例如

  • 每个测试应该是独立的,并且不受其他测试的影响
  • 每个单元测试应该测试一件事,而只能测试一件事
  • 单元测试不应访问数据库

等等。所有这些都是正确的,具体取决于您如何定义“单元测试”

我将“单元测试”定义为类似以下内容:“一种测试,它针对一个代码单元行使一项功能,与其他相关组件隔离开来”。

根据该定义,您所做的(如果需要在运行测试之前将记录添加到数据库中)根本不是“单元测试”,而是更多的通常称为“集成测试”的测试。(根据我的定义,真正的单元测试不会访问数据库,因此您无需在删除记录之前添加记录。)

一个集成测试将行使使用多个组件(例如,用户界面功能数据库),这将适用于单元测试的指导并不一定适用于集成测试。

正如其他人在回答中提到的那样,即使您做的事情与某些单元测试指南相违背,您所做的也不一定是错误的。相反,请尝试推断每种测试方法中实际要测试的内容,如果发现需要多个组件来满足您的测试,并且某些组件需要预先配置,请继续进行操作。

但最重要的是,要了解软件测试种类繁多(单元测试,系统测试,集成测试,探索性测试等),并且不要尝试将一种类型的指导应用于所有其他类型。


那么,您是说无法对从数据库中删除进行单元测试吗?
克里斯·

如果您要访问数据库,则(按定义)它是集成测试,而不是单元测试。因此,从这个意义上讲,不。您不能从数据库中删除“单元测试”。您可以进行单元测试的是,当要求您测试的代码删除一些数据时,它会与数据访问模块正确交互。
埃里克·金

但是关键是,有些人可能对“单元测试”的定义有所不同,因此在应用“单元测试”指南时必须小心,因为该指南可能不会像我们认为的那样适用。
埃里克·金

1

这就是为什么其他准则之一就是使用接口的原因。如果您的方法采用的是实现接口而不是特定类实现的对象,则可以创建不依赖于其余代码库的类。

另一种选择是使用模拟框架。这些使您可以轻松创建这些类型的伪对象,这些伪对象可以传递到要测试的方法中。可能必须为虚拟类创建一些存根实现,但是仍然会与实际实现以及测试涉及的内容分离。


1

如您所见,如果一项测试失败,那么另一项测试也会失败,因为它们都是必需的。

所以?

...这不违背单元测试的准则吗?

没有。

这样的案件应该如何处理?

多个测试可以独立进行,并且由于相同的错误而全部失败。这实际上是正常的。许多测试可能(间接地)测试某些常用功能。通用功能中断时,所有操作都会失败。没错。

单元测试精确地定义为类,因此它们可以轻松共享代码,例如用于测试更新和删除的公共虚拟记录。


1

您可以使用模拟框架或将“环境”与内存数据库一起使用。最后一个是一类,您可以在测试运行之前创建通过测试所需的所有内容。

我更喜欢最后一个-用户可以帮助您输入一些数据,以便您的测试变得最接近真实世界。


是的-但是您实际上并不是在这里测试真实的数据库连接。除非您以为那总是可行的,但前提是危险。
克里斯·
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.