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

时间:2020-03-05 18:46:10  来源:igfitidea点击:

人们如何对他们的业务应用程序进行单元测试?我已经看到了很多带有"简单测试"示例的单元测试示例。前任。一个计算器。人们如何对数据密集型应用程序进行单元测试?我们如何汇总样本数据?在许多情况下,一项测试的数据可能根本无法用于另一项测试,这使得仅拥有一个测试数据库变得很困难?

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

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

如果答案是没有一个很好的方法来解决这个问题,并且目前它只是些糟透了,那也很有用。

任何想法,想法,建议或者技巧都将受到赞赏。

解决方案

回答

对于使用相同逻辑但数据不同的大量不同运行,我们可以使用CSV,任意多的输入列和最后的输出列等。

回答

这取决于我们要测试的内容。如果我们要测试业务逻辑组件,那么它就无关紧要,数据来自何处,我们可能会使用模拟或者手动存根类来模拟该组件原本会调用的数据访问例程。我唯一弄乱数据访问的时间是在我实际测试数据访问组件本身时。

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

回答

听起来我们可能正在测试基于消息的系统,或者具有高度参数化接口的系统,其中输入数据的排列很多。

一般而言,标准统一测试的所有规则仍然适用:

  • 尝试使要测试的单元尽可能小且离散。
  • 尝试使测试独立。
  • 分解代码以分离依赖关系。
  • 使用模拟和存根替换依赖项(例如dataaccess)

完成此操作后,我们将消除测试中的许多复杂性,希望揭示出良好的单元测试集,并简化示例数据。

然后,仍然需要复杂的输入数据来编译用于测试的样本数据的一种好方法是正交测试,或者参见此处。

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

回答

模拟框架使我们能够测试业务对象。
数据驱动测试通常最终变得比单元测试更像是一个集成测试,它们还承担着管理测试执行前后的数据存储状态以及连接和执行查询所花费的时间的负担。

通常,我会避免进行单元测试,而该单元测试会涉及业务对象中的数据库。至于测试数据库,我们需要不同的策略。

话虽这么说,我们永远无法完全摆脱数据驱动的测试,而只能限制实际上需要调用后端系统的测试范围。

回答

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

我在这里有一个非常详细的帖子,关于集成测试数据访问层

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

关于对应用程序内部的业务逻辑进行单元测试,我还要引用@Phil和@Mark的注释,因为如果我们模拟出业务对象具有的所有依赖关系,则一次测试一个实体的应用程序逻辑变得非常简单;)

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

  • 1-单元测试将数据推入数据访问代码之前发生的逻辑。例如,如果我们有一些代码可以根据某些属性计算一些数字,则编写一个测试,仅检查该1函数的逻辑是否满足要求。模拟出对数据访问类的任何依赖关系,因此仅在测试应用程序逻辑时就可以忽略它。
  • 2-集成测试一旦我们获取了可操纵的数据(来自我们单元测试的先前方法)并调用适当的存储过程后发生的逻辑。在特定于数据的测试类中执行此操作,以便在完成后可以回滚。存储过程运行后,对数据库进行查询以获取对象,因为我们已经对数据进行了一些逻辑并验证其是否具有我们期望的值(存储过程逻辑/ etc)

如果我们需要在数据库中输入一个条目来运行存储过程,则只需在运行包含逻辑的存储过程之前插入该数据即可。例如,如果我们有需要测试的产品,则可能需要插入供应商和类别条目,因此在插入产品之前,请对供应商和类别进行快速而肮脏的插入,以便产品插入按计划进行。

回答

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

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

数据库连接测试

当我有连接到数据库的自动化测试时,通常会制作一个测试数据库模板,该模板具有足够的数据用于所有测试。运行自动化测试时,将从模板为每个测试生成一个新的测试数据库。由于测试经常会更改数据,因此必须不断重新生成测试数据库。添加测试后,我通常会将更多数据添加到测试数据库模板。

此测试方法有一些不错的优点。明显的好处是测试也可以使用模式。另一个优点是,在设置了初始测试之后,大多数新测试将能够重用现有的测试数据。这样可以轻松添加更多测试。

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

如果我们不能随意生成新的测试数据库,则这种测试风格显然行不通。

模拟持久层测试

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

使用模拟对象测试的好处是我们可以变得非常具体。通常,在没有模拟对象的自动化测试中,有些根本无法达到的执行路径。它们还使我们无需维护大量的整体测试数据。

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

模拟对象的缺点是它们通常会导致大量额外的测试代码。这并不可怕,因为在我们运行测试的次数中进行摊销时,几乎任何数量的测试代码都是廉价的,但是拥有比生产代码更多的测试代码可能会很烦人。