如何创建可扩展且无副作用的集成测试?


14

在当前的项目中,我很难找到一个好的解决方案来创建没有副作用的可扩展集成测试。关于副作用自由属性的一些澄清:它主要是关于数据库的;测试完成后,数据库中不应有任何更改(应保留状态)。也许可伸缩性和状态保存并没有融合在一起,但是我真的想推动一个更好的解决方案。

这是一个典型的集成测试(这些测试涉及数据库层):

public class OrderTests {

    List<Order> ordersToDelete = new ArrayList<Order>(); 

    public testOrderCreation() {
        Order order = new Order();
        assertTrue(order.save());
        orderToDelete.add(order);
    }

    public testOrderComparison() {
        Order order = new Order();
        Order order2 = new Order();
        assertFalse(order.isEqual(order2);
        orderToDelete.add(order);
        orderToDelete.add(order2);
    }
    // More tests

    public teardown() {
         for(Order order : ordersToDelete)
             order.delete();
    }
}

可以想象,这种方法产生的测试非常慢。而且,当将其应用于整个集成测试时,仅需花费5秒钟即可测试系统的一小部分。我可以想象当覆盖范围增加时,这个数字会上升。

编写此类测试的另一种方法是什么?我可以想到的一种替代方法是使用一种全局变量(在一个类中),并且所有测试方法都共享该变量。结果,只有很少的订单被创建和删除。导致更快的测试。但是,我认为这带来了更大的问题。这些测试不再是孤立的,并且越来越难以理解和分析它们。

可能仅仅是集成测试并不像单元测试那样频繁地运行。因此,低性能可能被那些人接受。无论如何,很高兴知道是否有人想出其他方法来改善可伸缩性。

Answers:


6

考虑使用Hypersonic或另一个内存数据库进行单元测试。测试不仅执行速度更快,而且副作用也无关紧要。(在每次测试后回滚事务也可以在同一实例上运行许多测试)

这也将迫使您创建数据模型,这对IMO来说是一件好事,因为这意味着生产数据库上发生的事情无法莫名其妙地开始使测试失败,并且它为您提供了一个“干净的数据库安装”起点”,这样会很有帮助,如果您需要在不连接到现有生产站点的情况下突然部署应用程序的新实例。

是的,我确实使用了此方法,是的,这是第一次设置PITA,但是在我将要完成的项目的整个生命周期中,这笔钱本身是值得的。还有许多工具可帮助进行数据模型化。


这主意听起来很不错。我特别喜欢它的完全隔离方面。从全新的数据库安装开始。似乎需要一些努力,但是一旦设置,将是非常有益的。谢谢。
Guven

3

这是每个人在编写集成测试时都面临的永恒问题。

理想的解决方案(尤其是在进行生产测试时)是在设置中打开一个事务,然后在拆卸时回滚它。我认为这应该适合您的需求。

在不可能的地方(例如,从客户端层测试应用程序的地方),另一种解决方案是在虚拟机上使用数据库,并在安装过程中拍摄快照,然后在拆卸时返回快照(它不会花费尽可能长的时间)。


我不敢相信我还没有想到交易回滚。我将把这个想法用作解决方案的快速解决方案,但是最终,内存数据库DB看起来非常有前途。
Guven

3

集成测试应始终针对生产设置运行。就您而言,这意味着您应该具有相同的数据库和应用程序服务器。当然,出于性能考虑,您可以决定使用内存数据库。

但是,您永远不要做的是扩大交易范围。有些人建议接管对交易的控制,并在测试后将其回滚。当您执行此操作时,所有实体(假设您正在使用JPA)在整个测试执行过程中保持与持久性上下文的连接。这可能会导致一些非常讨厌的错误,很难找到

相反,您应在每次测试后通过JPAUnit之类的库或类似的方法手动清除数据库(使用JDBC清除所有表)。

由于性能问题,您不应该在每个版本上都执行集成测试。让您的持续集成服务器执行此操作。如果使用Maven,则可能会喜欢使用故障安全插件,该插件可让您将测试分为单元测试和集成测试。

另外,您不应该嘲笑任何东西。请记住,您是集成测试,即在运行时执行环境中测试行为。


好答案。一点:模拟某些外部依赖关系(例如发送电子邮件)可能是可以接受的。但是您应该通过设置完整的模拟服务/服务器来模拟,而不是在Java代码中模拟它。
sleske 2011年

Sleske,我同意你的看法。特别是当您使用JPA,EJB,JMS或根据其他规范实施时。您可以交换应用程序服务器,持久性提供程序或数据库。例如,您可能想使用嵌入式Glassfish和HSQLDB来简单地设置和提高速度(当然,您可以自由选择其他经过认证的实现)。
BenR 2011年

2

关于可扩展性

之前我曾遇到过这个问题,集成测试花费的时间太长,对于单个开发人员连续不断地在变化的紧密反馈循环中运行不切实际。解决此问题的一些策略是:

  • 编写更少的集成测试 -如果您对系统中的单元测试有足够的了解,则集成测试应更侧重于(混合)集成问题,以及不同组件如何协同工作。它们更类似于烟雾测试,只是尝试查看系统是否仍可整体运行。因此,理想情况下,单元测试涵盖应用程序的大多数功能部分,而集成测试仅检查那些部分之间如何交互。您无需在这里广泛涵盖逻辑路径,只需通过系统的一些关键路径即可。
  • 一次只运行一部分测试 -作为一个开发人员从事某些功能的开发人员,您通常会感觉到哪些测试需要涵盖该功能。仅运行那些有意义的测试以涵盖您的更改。诸如JUnit之类的框架允许您将灯具分组。创建分组,以便为​​您拥有的每个功能提供最佳覆盖。当然,您仍然希望在某个时候运行所有集成测试,也许是在提交到源代码控制系统之前,当然还要在连续集成服务器上。
  • 优化系统 -集成测试和一些性能测试可能会为您提供必要的输入,以调整系统的较慢部分,以便以后运行测试更快。这可能有助于发现数据库中所需的索引,子系统之间的闲聊接口或其他需要解决的性能瓶颈。
  • 并行运行测试 -如果您有一组很好的正交测试,则可以尝试并行运行它们。注意我前面提到的正交要求,您不希望您的测试步入彼此。

尝试结合使用这些技术以获得更大的效果。


1
真的很好的指导方针;减少编写集成测试的确是最好的建议。另外,并行运行它们将是一个很好的选择。完美的“测试”,以检查我是否在隔离方面直接获得了测试。
Guven

2

出于测试目的,我们使用了基于文件的SQLite数据库部署(只需复制资源)。这样做是为了让我们也可以测试架构迁移。据我所知,架构更改不是事务性的,因此在事务被取消后不会回滚。同样,不必依赖事务支持来进行测试设置,从而可以正确地测试应用程序中事务的行为。

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.