如何对业务应用程序进行单元测试?

时间:2008-09-01 23:21:34

标签: unit-testing

人们如何对其业务应用程序进行单元测试?我已经看到很多单元测试的例子都是“简单测试”的例子。防爆。一个计算器。人们如何对数据量大的应用进行单元测试?你是如何整理样本数据的?在许多情况下,一个测试的数据可能根本不适用于另一个测试,这使得很难只拥有一个测试数据库?

测试代码的数据访问部分非常简单。它正在测试所有针对似乎难以测试的数据的方法。例如,想象一个发布过程,其中存在大量数据访问以确定发布的内容,数字被调整等。发生了许多临时步骤(并且需要进行测试)以及之后的测试以确保发布是成功的。其中一些步骤实际上可能是存储过程。

过去我曾尝试将测试数据插入测试数据库,然后运行测试,但老实说,编写这种代码非常痛苦(并且容易出错)。我还尝试过预先构建测试数据库并回滚更改。这样做没问题但是在很多地方你也不能轻易做到这一点(很多人会说这是集成测试;所以,我仍然需要能够以某种方式测试它。)

如果答案是没有一种很好的方法来解决这个问题,而且目前只是很糟糕,那么知道也是有用的。

任何想法,想法,建议或提示都表示赞赏。

6 个答案:

答案 0 :(得分:6)

我的自动功能测试通常遵循以下两种模式之一:

  • 数据库连接测试
  • 模拟持久层测试

数据库连接测试

当我有连接到数据库的自动化测试时,我通常会创建一个测试数据库模板,该模板具有足够的数据用于所有测试。运行自动化测试时,将为每个测试从模板生成一个新的测试数据库。必须不断重新生成测试数据库,因为测试通常会更改数据。随着测试的增加,我通常会将更多数据附加到测试数据库模板中。

这种测试方法有一些很好的优点。显而易见的优点是测试还可以运用您的架构。另一个优点是,在设置初始测试后,大多数新测试将能够重用现有的测试数据。这样可以轻松添加更多测试。

缺点是测试数据库会变得笨拙。因为数据通常会在一次添加一个测试,所以它会不一致,甚至可能不切实际。当数据库模式发生重大变化时,你最终会诅咒设置测试数据库的人(对我而言,这通常意味着我最终诅咒自己)。

如果您无法随意生成新的测试数据库,这种测试方式显然不起作用。

模拟持久层测试

对于此模式,您可以创建与测试用例一起使用的mock objects。这些模拟对象拦截对数据库的调用,以便您可以以编程方式提供适当的结果。基本上,当您测试的代码调用findCustomerByName()方法时,将调用模拟对象而不是持久层。

使用模拟对象测试的好处是你可以非常具体。通常,有些执行路径在无模拟对象的自动化测试中无法实现。它们还使您无法维护大量的整体测试数据。

另一个好处是缺乏外部依赖性。因为模拟对象模拟持久层,所以测试不再依赖于数据库。在选择要选择的模式时,这通常是决定性因素。在处理具有严格许可条款的旧数据库系统或数据库时,模拟对象似乎更具吸引力。

模拟对象的缺点是它们经常导致大量额外的测试代码。这并不可怕,因为几乎任何数量的测试代码在运行测试的次数分摊时都很便宜,但是生成代码后会有更多测试代码,这会很烦人。

答案 1 :(得分:2)

这取决于您正在测试的内容。如果你正在测试一个业务逻辑组件 - 那么它的数据来自于它的无关紧要,你可能会使用模拟或手动存根类来模拟组件在野外调用的数据访问例程。我搞乱数据访问的唯一一次是我实际上正在测试数据访问组件。

即便如此,我倾向于在TestFixtureSetUp方法中打开一个数据库事务(显然这取决于你可能使用的单元测试框架)并在测试套件TestFixtureTeardown的末尾回滚事务。

答案 2 :(得分:2)

Mocking Frameworks使您可以测试业务对象。 数据驱动测试通常最终变成集成测试而不是单元测试,它们还带来了在测试执行前后管理数据存储状态的负担以及连接和执行查询所花费的时间。 / p>

一般情况下,我会避免进行从业务对象触及数据库的单元测试。至于测试数据库,你需要一个不同的策略。

话虽如此,你永远无法完全摆脱数据驱动测试,只能限制实际需要调用后端系统的测试数量。

答案 3 :(得分:2)

当我尝试使用回滚解决方案进行这些集成测试时,我必须在@Phil Bennett的评论中加以评论。

我有一篇关于集成测试数据访问层here

的非常详细的帖子

我不仅展示了示例数据访问类,基类和示例数据库事务夹具类,还展示了一个完整的CRUD集成测试,其中显示了示例数据。使用这种方法,您不需要多个测试数据库,因为您可以控制每次测试的数据,并且在测试完成后,事务都会回滚,因此您的数据库是干净的。

关于单元测试应用程序内部的业务逻辑,我还会通过@Phil和@Mark来评论注释,因为如果你模拟了业务对象所具有的所有依赖关系,那么在一个实体上测试你的应用程序逻辑就变得非常简单了。时间;)

编辑:那么你是否正在寻找一个巨大的集成测试,它将验证从逻辑预数据库/存储过程运行w /逻辑的所有内容,最后验证回来的路径?如果是这样,你可以将其分为两步:

  • 1 - 单元测试推送数据之前发生的逻辑 进入您的数据访问代码。对于 例如,如果您有一些代码 基于计算一些数字 一些属性 - 写一个测试 只检查是否有逻辑 这个1功能可以满足您的要求 它要做。嘲笑任何依赖 在数据访问类上,你可以 忽略它的测试 单独应用逻辑。

  • 2 - 集成测试一旦你拿走了你的逻辑     操纵数据(来自之前的     我们单元测试的方法)并调用     适当的存储过程。做     这是一个数据特定的测试     class,所以你可以回滚它     完成。存储后     程序已运行,请执行查询     反对数据库来获取你的     现在我们做了一些事情     反对数据的逻辑并验证它     具有您期望的值     (后存储过程逻辑/等)

如果在数据库中需要一个条目来运行存储过程,只需在运行其中包含逻辑的sproc之前插入该数据。例如,如果您有需要测试的产品,则可能需要插入供应商和类别条目,因此在插入产品之前,请为供应商和类别执行快速且脏的插入,以便您的产品插件按计划运行。 / p>

答案 4 :(得分:1)

听起来您可能正在测试基于消息的系统或具有高度参数化接口的系统,其中存在大量输入数据的排列。

一般而言,标准unti测试的所有规则仍然有效:

  • 尽量使被测单元尽可能小而离散。
  • 尝试独立进行测试。
  • 分解依赖关系的因子代码。
  • 使用模拟和存根替换依赖项(如dataaccess)

完成此操作后,您将从测试中删除大量复杂性,希望能够显示出良好的单元测试集,并简化样本数据。

然后编译样本数据以进行测试仍然需要复杂的输入数据的良好方法是Orthogonal testing,或者参见here

我已经使用这种方法为WCF和BizTalk解决方案生成测试计划,其中输入消息的排列可以创建多个可能的执行路径。

答案 5 :(得分:0)

对于相同逻辑但可以使用不同数据的许多不同运行,您可以使用CSV,输入的列数和输出的最后一列等。