集成测试中的模拟

43
如何模拟集成测试所需的众多依赖项?
我在我的“纯”单元测试中使用Mockito。在这种情况下,“纯”意味着测试一个单独的类,以模拟其所有依赖项。现在来看看集成测试。假设在这种情况下,集成测试将测试以下内容:
1. 将消息放入队列中 2. 处理消息 3. 将响应消息放入响应队列
让我们还假设,在步骤2中发生的处理是非常重要的事情。它依赖于大量的数据库交互、多个外部服务、文件系统等等。此外,流程将触发许多副作用,因此我不能仅仅确保响应是正确的-我需要验证副作用。
每个依赖项都由一个单一的无状态服务类包装,这使它们易于进行模拟。
人们如何处理这种情况?
我很想使用Mockito,以便能够验证上述流程的副作用。然而,Mocktio的文档(在很大程度上也是其实现)似乎强烈反对在除“纯”单元测试以外的上下文中使用它。我尝试过这种方法,但是:
1. 很难填充存根数据(因为有很多数据) 2. 很难让Spring将这些存根实例注入到我的bean中 3. 很难“重置”模拟,以便我可以验证不同的交互集而不清除存根。
编辑
我知道我可以使用类似于HSQLDB实例之类的东西来处理数据库问题,但仍然存在外部服务的问题。为了重复性,我无法指望这些服务处于运行状态,处于我需要的状态等等。我唯一看到的选择是模拟它们。
你会怎么做?

1
只是为了澄清,集成测试可以有两种方式。听起来你的意思是指测试连接组件是否良好协同工作(基本上是测试API)。但是,有时集成是指端到端,因此您不会模拟服务并实际允许其访问数据库。这个澄清可以帮助回答问题。请参见https://dev59.com/kG445IYBdhLWcg3wWZAU以进一步澄清类型。 - Justin Pihony
这确实适用于任何需要大量外部依赖的测试(集成或端到端)。例如,我可以用HSQLDB实例替换我的数据库存根,但我仍然有所有其他服务。将稍微编辑问题以澄清。 - Roy Truelove
那么你最终是如何解决模拟问题的? - Pupsik
3个回答

14

好问题。

看起来你已经达到了Mockito的限制。Mockito非常适合检查对象交互。

然而,你想要的似乎是在更高层面上的可观察性(和可控性)。恐怕你需要为此设计并手工创建所需的mocks或stubs。

在单元测试级别上,这些mocks可以通过Mockito轻松生成。在集成测试级别上,这变得更加困难,你需要专门设计的可测试接口。


1
是的,听起来在集成层面上,人们会“尽力而为”,要么通过使用模拟库做他们能做的事情,要么实际上访问服务/数据库的测试实例。我目前正在开发一个基于Groovy的Mockito扩展,以帮助集成测试变得更加轻松一些。 - Roy Truelove

8
为了模拟像数据库,Web服务,文件系统等外部服务,您可能需要进行一些重构。对于每个外部服务,您应编写一个包装类,其中每个操作都有一个方法,您希望执行该操作。这样的每种方法都不应具有实际逻辑,而只需以外部服务将理解的方式传递其参数,并返回包含外部服务返回的任何数据的对象。例如,如果您正在与数据库交互,则包装类可能会将其参数格式化为SQL语句,将其提交到现有Connection对象中,并为结果返回List
由于包装类的方法不包含逻辑(即没有if/else,循环和异常处理),因此无需对包装类进行单元测试。您应该对包装类进行集成测试,以确保其职责被正确执行(即SQL语句对数据库产生所需的效果)。
现在重新编写与外部服务交互的类,使它们与包装类交互。然后很容易对它们进行单元测试-您只需模拟包装类即可。

1
谢谢David。我的外部服务已经很好地抽象出来了-每个服务都包装在一个单例Spring服务类中。即使如此,在复杂的情况下,模拟变得困难。有许多需要存根的数据,通常会多次调用相同的服务方法,这意味着我需要让存根返回多个值,然后还有将它们与Spring注入的问题...变得混乱。我必须假设人们已经以更清晰的方式解决了这个问题。 - Roy Truelove
它们必须用单例包装吗?单例在模拟测试中往往很难处理。你能否设计使得包装器不是单例的呢? - Dawood ibn Kareem
此外,我想询问为什么在一个测试中会多次调用同一个服务方法。也许你的测试方法太大了?我坚信“每个测试方法只有一个断言”的规则;这确实可以使测试更加清晰。 - Dawood ibn Kareem
关于单例模式 - 它们是由Spring注入的,而不是旧的“static getInstance()”类型。这使得它们非常容易测试。请参见:https://dev59.com/YnE95IYBdhLWcg3wdtqj#2302246。此外,帖子的重点正是为了处理大型测试。我有一套小型的“每个测试方法一个断言”的测试套件,但现实情况是,在复杂系统的集成测试中,将多次调用相同的服务方法。 - Roy Truelove
抱歉,你提到这是一个大型集成测试。所以请忽略我关于每个测试只有一个断言的评论。我的经验是Mockito非常适合进行大型集成测试。我假设你知道如何让Mockito在对同一模拟方法进行连续调用时执行不同的操作?很抱歉我无法提供更多帮助。 - Dawood ibn Kareem

-2
如果有一些http或rest模拟框架,使用它应该是很好的选择。
所有复杂的依赖关系都可以被记录、修改和重放。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接