关于单元测试:如何使用Mocks?

关于单元测试:如何使用Mocks?

How are Mocks meant to be used?

当我最初被介绍给Mocks时,我觉得主要目的是模拟来自外部数据源的对象。这样,我不必维护自动化的单元测试测试数据库,我可以伪造它。

但是现在我开始有所不同了。我想知道Mocks是否可以更有效地将测试方法与自身之外的任何事物完全隔离。不断浮现的图像是您在绘画时使用的背景。您想防止涂料泛滥成灾。我只是在测试该方法,我只想知道它对这些伪造的外部因素有何反应?

以这种方式进行操作似乎非常繁琐,但是我看到的好处是,如果测试失败,这是因为它被拧紧了,而不是向下拧了16层。但是现在我必须进行16个测试才能获得相同的测试覆盖率,因为每个测试都将单独进行测试。另外,每个测试都变得更加复杂,并且与所测试的方法更加紧密地联系在一起。

对我来说这是对的,但它看起来也很残酷,所以我有点想知道别人的想法。


我建议您看看Martin Fowler的文章Mocks Are n't Stubs,以获得对Mocks更具权威性的治疗方法。

模拟的目的是对依赖关系进行隔离的单元测试代码,以便您可以在"单元"级别上真正地测试一段代码。被测试的代码是真正的交易,它所依赖的其他所有代码段(通过参数或依赖项注入等)都是"模拟"(空的实现,在调用其方法之一时始终返回期望值)。

一开始的模仿似乎很乏味,但是一旦掌握了使用技巧,它们就会使单元测试变得更加轻松和强大。大多数语言都有Mock库,这使得嘲笑变得相对琐碎。如果您使用的是Java,我将推荐我最喜欢的个人软件:EasyMock。

让我结束这个想法:您也需要集成测试,但是进行大量的单元测试可以帮助您找出存在错误的组件。


别走卢克大师的黑暗之路。 :)不要嘲笑一切。您可以,但是不应该...这就是原因。

  • 如果您继续单独测试每种方法,那么在将它们全都放在BIG BANG上时,您会感到惊讶和艰巨的工作。我们构建对象,以便它们可以一起解决更大的问题。就其本身而言,它们是微不足道的。您需要知道所有协作者是否都按预期工作。
  • 嘲讽通过引入重复来使测试变脆-是的,我知道这听起来令人震惊。对于您希望设置的每个模拟,在方法签名存在的位置有n个。实际代码和您的模拟期望(在多个测试中)。更改实际代码更容易...更新所有模拟期望是乏味的。
  • 您的测试现在可以了解内部实施信息。因此,您的测试取决于您选择实施解决方案的方式……不好。测试应该是可以由多种解决方案满足的独立规范。我应该可以自由地在代码块上按Delete键并重新实现,而不必重写测试套件。因为要求仍然保持不变。

关闭时,我会说:"如果它像鸭子一样嘎嘎叫,走路像鸭子一样,那么它可能就是鸭子"-如果感觉不对。 *使用模拟来抽象出有问题的孩子,例如IO操作,数据库,第三方组件等。像salt一样,其中有些是必要的..太多和:x *
这是基于状态与基于迭代的测试之间的圣战。.谷歌搜索将为您提供更深入的了解。

说明:我遇到了一些阻力。这里进行集成测试:)因此,要阐明我的立场。.

  • 在"验收测试" /集成领域中没有模拟。您只会在"单元测试"世界中找到它们。.这就是我在这里关注的重点。
  • 验收测试是不同的,非常需要-不要轻视它们。但是单元测试和验收测试是不同的,应该保持不同。
  • 组件或包装内的所有协作者都无需彼此隔离。就像过度优化的微观优化一样。它们的存在是为了共同解决一个问题。

是的,我同意。我认为模拟有时会很痛苦,但通常是必须的,这样才能使您的测试真正成为单元测试,即,只有可以使您的测试涉及的最小单元正在测试中。这使您可以消除可能影响测试结果的任何其他因素。您最终会进行更多的小型测试,但是要弄清楚代码存在问题的地方变得非常容易。


发明了模仿的部分原因是为了回答以下问题:如果对象没有吸气剂或吸气剂,您将如何对它们进行单元测试?

这些天,推荐的做法是模拟角色而不是对象。使用Mocks作为设计工具来谈论协作和职责分离,而不是"智能Stubbing"。


就像之前说过的那样,如果您模拟所有内容以隔离比您要测试的类更详细的内容,那么您将放弃在正在测试的代码中强制执行内聚。

请记住,模拟具有基本优势,即行为验证。这是Stubbing不提供的东西,也是导致测试更脆弱(但可以提高代码覆盖率)的另一个原因。


是的,在某种程度上,模拟旨在用于模拟外部数据源,例如数据库或Web服务。然而,如果要设计松散耦合的代码,则可以在更细粒度的范围内,几乎可以任意地在代码中画线,以了解在任何时候可能是"外部系统"。接受我当前正在处理的项目:

当某人尝试签入时,CheckInUi将CheckInInfo对象发送到CheckInMediator对象,该对象使用CheckInValidator对其进行验证,然后,如果确定,它将使用CheckInInfoAdapter用CheckInInfo填充名为Transaction的域对象,然后将Transaction传递给ITransactionDao.SaveTransaction()的持久性实例。

我现在正在编写一些自动化集成测试,显然CheckInUi和ITransactionDao是外部系统的窗口,应该嘲笑它们。但是,有谁说在某个时候CheckInValidator不会调用Web服务?这就是为什么在编写单元测试时,您会假设除类的特定功能之外的所有内容都是外部系统。因此,在CheckInMediator的单元测试中,我模拟了它要与之通信的所有对象。

编辑:Gishu在技术上是正确的,并非所有内容都需要模拟,我不模拟模拟CheckInInfo,因为它只是数据的容器。但是,您可能会视为外部服务的任何东西(几乎是任何可以转换数据或具有副作用的东西)都应该被嘲笑。

我喜欢一个比喻,就是将一个适当松散耦合的设计想像成一个领域,周围的人站在旁边玩一场接球比赛。当有人传球时,他可能会向下一个人投掷完全不同的球,他甚至可能会连续向不同的人投掷多个球,或者投掷一个球,并等待将其返回,然后再将其投掷给另一个人。这是一个奇怪的游戏。

现在,作为他们的教练和经理,您当然想检查一下整个团队的运作方式,以便进行团队练习(集成测试),但是您还需要让每个球员自己对付逆止器和投球机(模拟的单元测试)。该图片丢失的唯一片段是模拟预期,因此我们的球涂有黑色焦油,因此当它们撞到支撑架时会弄脏支撑架。每个支持器都有一个人要针对的"目标区域",如果在练习结束时目标区域内没有黑标,则说明您知道出了点问题,并且该人需要调整其技术。

我花些时间正确地学习它,那天我才知道Mocks是一个巨大的时刻。将其与控制容器的反转结合使用,我再也不会回头了。

顺便提一句,我们的一位IT人员刚进来,给了我一台免费的笔记本电脑!


是的,这是使用模拟进行测试的缺点。您需要做很多工作,因为它感觉很残酷。但这是单元测试的本质。如果您不嘲笑外部资源,如何隔离测试?

另一方面,您正在嘲笑缓慢的功能(例如数据库和I / O操作)。如果测试运行得更快,那将使程序员满意。没有什么比等待真正缓慢的测试更痛苦的了,在您尝试实现一项功能时,它要花费超过10秒钟才能完成运行。

如果项目中的每个开发人员都花时间编写单元测试,那么这16层(间接)不会有太大的问题。希望您应该从一开始就拥有该测试覆盖率,对吗? :)

此外,不要忘记在协作对象之间编写功能/集成测试。否则,您可能会错过一些东西。这些测试不需要经常运行,但仍然很重要。


我的理念是,您应该编写可测试的代码以适合测试,
不编写适合代码的测试。

对于复杂性,我认为测试应该编写简单,仅仅是因为您编写了更多测试。

如果您要模拟的类没有测试套件,我可能会认为这是一个好主意,因为如果它们具有正确的测试套件,您会知道问题出在何处而没有隔离。

我在模拟对象上使用的大多数时间是当我正在为其编写测试的代码紧密耦合(阅读:不良设计)时,我不得不在它们所依赖的类不存在时编写模拟对象可用的。当然,可以有效地使用模拟对象,但是如果您的代码需要使用它们,那么我将再来看一下设计。


模拟对象是(1)经常用作隔离测试代码的一种方法,但keithb已经指出2)对于"关注协作对象之间的关系"很重要。本文提供了与以下主题有关的一些见解和历史:具有模拟对象的责任驱动设计。


推荐阅读

    miui模拟器电脑版|MIUI模拟器

    miui模拟器电脑版|MIUI模拟器,,1. MIUI模拟器笔记本电脑没有办法刷MIUI系统。MIUI系统是小米为手机研发的,基于安卓系统的手机用系统,笔记本

    拍拍模拟器电脑版|拍拍猴模拟器

    拍拍模拟器电脑版|拍拍猴模拟器,,1. 拍拍猴模拟器爬猴,5173,淘宝,拍拍,慧聪,易宝,送宝挺多的!我比较信任爬猴的 2. 拍拍拍模拟器游戏ppsspp还是相

    模拟器设置vt|模拟器设置VT

    模拟器设置vt|模拟器设置VT,,模拟器设置vtwin7安卓模拟器vt开启步骤如下:1.开启VT需要进入BOIS进行设置。首先重启电脑,在电脑启动时不停地

    常识硬件的计算机日常维护

    常识硬件的计算机日常维护,,硬件(防尘、防高温、防磁、防潮、防静电、防震) 应将电脑放在一个干净的房间,避免灰尘太多造成的不利影响,对各种

    移动硬盘如何使用移动硬盘维护知识

    移动硬盘如何使用移动硬盘维护知识,,现在移动硬盘的广泛使用和快节奏的工作使拆迁的一部分;驱动;人,我们说不;拆除;拆除手段,在硬盘有意无意的操

    Python之可迭代对象、迭代器、生成器

    Python之可迭代对象、迭代器、生成器,迭代,生成器,一、概念描述可迭代对象就是可以迭代的对象,我们可以通过内置的iter函数获取其迭代器,可

    应用程序对象

    应用程序对象,,应用程序对象是一个应用程序级对象,用于在所有用户之间共享信息,并且在Web应用程序运行期间可以保存数据。 应用的性质: 方法

    台式电脑维护维修|台式电脑维修教程

    台式电脑维护维修|台式电脑维修教程,,1. 台式电脑维修教程一,保修没过的话送修;二,已过保修的话,一般也很难找的到会修电源的电脑店,电脑配置较