代码之家  ›  专栏  ›  技术社区  ›  Paul Mrozowski

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

  •  14
  • Paul Mrozowski  · 技术社区  · 17 年前

    人员单元如何测试其业务应用程序?我已经看到很多使用“简单到测试”示例进行单元测试的示例。例如计算器。人员单元如何测试数据量大的应用程序?你如何整理你的样本数据?在许多情况下,一个测试的数据可能对另一个测试根本不起作用,这就使得仅仅拥有一个测试数据库变得困难?

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

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

    如果答案是没有一个很好的方法来处理这个问题,而且它目前只是有点糟糕,那么了解这个问题也很有用。

    任何想法、想法、建议或建议都会受到赞赏。

    6 回复  |  直到 17 年前
        1
  •  2
  •   Community Mohan Dere    8 年前

    当我试图用回滚解决方案来处理这些集成测试时,我不得不附和@phil bennett的评论。

    我有一篇关于集成测试数据访问层的非常详细的文章 here

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

    关于在您的应用程序中测试业务逻辑的单元,我也会附和@phil和@mark的评论,因为如果您模拟了业务对象的所有依赖项,那么一次测试一个实体的应用程序逻辑就变得非常简单;)

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

    • 1-单元测试推送数据前发生的逻辑 输入数据访问代码。为了 例如,如果您有一些代码 根据以下公式计算一些数字 一些属性——编写一个测试 只检查逻辑是否 这个1函数按您的要求执行 要做的。摆脱任何依赖 在数据访问类上,以便 忽略此项测试 仅应用程序逻辑。

    • 2-集成测试一旦您 操作数据(来自上一个 方法)并调用 适当的存储过程。做 在特定于数据的测试中 类,以便在 完整的。在你储存之后 过程已运行,请执行查询 根据数据库获取 反对我们已经做了一些 根据数据进行逻辑验证 具有您期望的值 (存储过程后逻辑/etc)

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

        2
  •  6
  •   Jay Stramel    17 年前

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

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

    数据库连接的测试

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

    这种测试方法有一些很好的优点。明显的优点是测试也可以运行您的模式。另一个优点是,在设置初始测试之后,大多数新测试将能够重新使用现有的测试数据。这使得添加更多测试变得容易。

    缺点是测试数据库将变得笨拙。因为数据通常会一次添加一个测试,所以它是不一致的,甚至可能是不现实的。当数据库模式发生重大更改时,您还将结束对设置测试数据库的人的诅咒(对我来说,这通常意味着我结束了对自己的诅咒)。

    如果您不能随意生成新的测试数据库,这种类型的测试显然不起作用。

    模拟持久层测试

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

    使用模拟对象测试的好处在于您可以得到非常具体的测试。通常情况下,在没有模拟对象的自动化测试中,有一些执行路径是无法达到的。它们还使您免于维护一组庞大的、单一的测试数据。

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

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

        3
  •  2
  •   Phil Bennett    17 年前

    这取决于你在测试什么。如果您正在测试一个业务逻辑组件——那么数据来自何处无关紧要,您可能会使用模拟或手动滚动的存根类来模拟组件在野外调用的数据访问例程。我处理数据访问的唯一时间是实际测试数据访问组件本身。

    即使这样,我还是倾向于在testfix特性设置方法中打开一个db事务(显然这取决于您可能使用的单元测试框架),并在测试套件testfix特性设置结束时回滚该事务。

        4
  •  2
  •   Mark Harris    17 年前

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

    一般来说,我会避免进行单元测试,这些测试会接触到来自业务对象的数据库。至于测试数据库,您需要不同的策略。

    这就是说,您永远无法完全摆脱数据驱动的测试,只会限制实际需要调用后端系统的测试数量。

        5
  •  1
  •   David Hall    17 年前

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

    一般来说,标准的所有规则直到测试仍然有效:

    • 试着使被测单元尽可能小而离散。
    • 尝试使测试独立。
    • 分离依赖关系的因子代码。
    • 使用mock和stub替换依赖项(如dataaccess)

    一旦完成这项工作,您就可以从测试中消除很多复杂性,希望能够显示出良好的单元测试集,并简化示例数据。

    对于仍然需要复杂输入数据的测试,一个很好的方法是编译样本数据 Orthogonal testing 或见 here .

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

        6
  •  0
  •   SCdF    17 年前

    对于在同一逻辑上运行的许多不同的操作,但是使用不同的数据,您可以使用csv,尽可能多的列用于输入,最后一列用于输出等。