| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 开发测试 -> 《有效的单元测试》第三章 续 -> 正文阅读 |
|
[开发测试]《有效的单元测试》第三章 续 |
这就是测试间谍的一切。像其他测试替身样,你将它们传入?。然后你令测试间谍?记录已发送的消息,并?让测试询问测试间谍是否收到指定消息。干得漂亮! 简而言之,测试间谍是一种测试替身,它用于记录过去发生的情况,这样测试在事后就知道所发生的一切。有时我们进一步利用这个概念,于是测试间谍就变成了全能的模拟对象。如果测试间谍像个卧底警察,那么模拟对象就像渗人暴民的远程控制机器人。这可能需要一些解释.... 3.2.4模拟对象反对惊喜 模拟对象(简称Mock)是特殊的Spy。它是一个在特定情景下可配置行为的对象。例如,UserRepository 接口的模拟对象可能被告之:当带着参数123调用findById0时要返回null,而当带着参数124调用findByld0时要返回User的一个实例。在这一点上,我们主要讨论的是根据参数来对特定的方法调用打桩。 如果一旦任何意外发生时Mock就立即使测试失败,Mock就能够变得更加精确。例如,假设我们告诉了模拟对象如何应对带着123或124的fndByld0调用,它就会严格按照指令工作。对于任何其他的调用一 不论是调用不同的方法或者带着另外的参数调用findByld0)-Mock就会抛出异常,直接使测试失败。同样,如果findByIdO被调用太多次,Mock 就会抱怨除非我们告诉它允许调用任意次数一 如果预期的调用没 发生,Mock 也会抱怨。 包括JMock、Mockito 和EasyMock在内的模拟对象库已经是成熟的工具了,崇尚测试的程序员可以借助它们获得力量。每个库都有自已的行事风格,但基本上你可以用它们中任何一个来完成所有的工作。 这并非模拟对象库的全面教程,但是我们迅速看看代码清单3.7中的例子,它展示了这种库的具体用法。这里我们使用JMock,因为我碰巧有个项目正在使用JMock。 代码清单3.7 JMock允许你在运行时配置mock
在这样一小段测试代码中,这个例子展示了许多模拟对象库用法的典型构造。首先,我们告诉库要为指定接口创建一个 模拟对象。 在contex.checking(中看似笨拙的代码块其实是测试在指导模拟的Internet,告诉它应该期待哪些交互,以及如何应对这些交互。这种情况下,我们预期测试会带着包含"langpair- en%7Cf"字符串的参数调用get(方法一次,对此,mock 应当返回指定字符串。 最终,我们将Mock传给被测的Translator对象,执行Translator,然后断言Translator为我们的场景提供了正确的翻译。 然而,这并非我们的全部断言。如前所述,Mock 可以严格地判断已经发生的预期交互。在模拟Internet的例子中,Mock 严格地断言它确实收到了一次带有指定子字符串参数的get()方法调用。 3.3使用测试替身的指南 测试替身是程序员的工具,就像木匠的锤子和钉子。存在敲钉子的适当方式,当然也有不恰当的方式一最好 是能把它们识别出来。 先从我认为最重要的指南开始吧,当你求助于测试替身时要时刻牢记它一从你的工具箱中选择合适的工具。 3.3.1为测试挑选合适的替身 有许多测试替身可供选择,它们看起来各有千秋。采用它们的最佳条件是什么?到底应该选择哪个? 这里并没有太多的硬性规定,但一般来说你应该因地制宜地混合使用。我是说,某些情况下你只想要“-一个返回5的对象”,而其他情况下你特别想知道某个方法被调用过。有时在一个测试中对两者都感兴趣,于是你将Stub、Fake 和Mock一同使用。 前面已经说过,并没有清晰的原则来决定采用哪种方式以得到最可读的测试。但我还是忍不住对如何选择这个问题阐述一些逻辑 和启发: ●如果你关心某些交互,即两个对象之间的方法调用,你可能会需要一个模拟对象Mock。 ●如果你决定使用Mock,但测试代码最终看起来不像你想的那样漂亮,那就看看一个手工的简单测试间谍Spy能否满足需要。 ●如果你只关心协作对象向被测对象输送的响应,用桩Stub就可以。 ●如果你想运行一个复杂场景,其中它所依赖的服务或组件无法供测试使用,而你对所有交互打桩的快速尝试却戛然而止,或产出了难以维护的糟糕的测试代码,那就考虑实现一个伪造对象Fake吧。 ●如果上述都不能满足你手上的特殊情况,那就抛硬币吧一正 面代表Mock,反面代表Stub,如果硬币直立,我允许你找一个Fake帮你干活。 如果觉得那个列表太难记,别怕。《JUnit Recipes》( Manning, 2004)的作者J. B.Rainsberger有一个简单的记忆规则,用于选择正确的测试替身类型: Stub 管查询,Mock 管操作。现在我们顺利地得到启发,知道什么时候该用哪种测试替身了,接下来看看如何使用它们。 3.3.2准备、 执行、断言 关于编码约定( convention),我要说几句。问题是各种标准太多了。幸运的是,当你构造单元测试时,存在一个大多数程序员都认为合理的、相当确定的实践。它叫做准备-执行一断言( Arrange-Act-Assert),这种组织测试的方式基本上是这样的,先准备用于测试的对象,然后触发执行,最后对输出进行断言。 代码清单3.8复制了代码清单3.7的测试,我们看它如何符合这种约定来组织测试方法。 代码清单3.8准备- 执行-断言使结构变得清晰
注意我在三段代码之间增加空白的方式。这用来强调三段代码的不同角色。 测试的前五行是准备?所要用到的协作对象。虽然其中我们只涉及Internet 接口的一个Mock,但是在测试开头设置多个协作者的情况也很常见。然后是被测对象Translator对它的实例化也是准备工作的一部分。。 从下一段代码中,我们调用translation?(被测的翻译功能),最后,不论预期输出是直接输出还是造成的副作用,我们都对它进行断言?。 给定-当-那么( Given, When, Then ) 行为驱动开发运动所推广的词汇和结构与“准备-执行一断言”很像:给定(某个上下文),当(发生某些事情),那么(期望某些结果)。这个想法以更加直观的语言来指定预期行为,尽管“准备一执行一断言”更好记,但“给定-当-那么”更流畅,使人们更加自然地思考行为(而不是实现细节)。 这种结构相当普遍,它有助于使测试保持专注。如果感觉三部分中某一部分很“大”,那就是一个信号,表明测试可能试图做太多事情,需要更加专注。既然说到这话题,咱们就简单讨论一下测试应该专注什么。 3.3.3检查行为 ,而非实现 人都会犯错误。模拟对象库新手常犯的一个错误是过度细致地对Mock设置期望。我指的是在测试中,对测试可能涉及的每个对象都做Mock,每个对象间的方法调用都严格指定。 是的,某种意义上,测试给予我们确定性,只要有任何变更它就会中断并报警。而这也是问题所在——即使 是最小的变更,哪怕它与测试所要验证的不相关,也会中断测试。好比在一片口香糖上密密麻麻地敲了许多钉子,使之动弹不得。 这种测试的基本问题是缺乏专注。一个测试应当只测试件事情,并好好地测试,清晰地沟通自己的意图。看着被测对象,你要问自己到底什么是想要验证的预期行为?至于实现细节,倒是并不需要钉在我们的测试中。 预期行为应该配置在Mock对象的期望中。应该寻求通过Stub或非严格Mock来提供实现细节,它们不介意交互从未发生或者发生多次。 检查行为,而非实现。当你掏出喜爱的模拟对象库时,你应该牢牢记住这一一点。说到这..... 3.3.4 6挑选你的工具 说到模拟对象库,Java程序员真是占了大便宜一有 太多可以选择的。像我之前提到的,你几乎可以用任何先进的库来做同样的事情,但是它们在API方面还是有些细微的区别以及一些独特的功能,从而在满足某些特定方向和需求时能够锤定音。 或许其中最独特的功能就是Mockito的打桩与验证分离。这得细说,接下来看个例子,我用Mockito重写了之前采用JMock的测试:
Mockito的API比JMock更简洁。除此之外,看起来差不多,是不是?是的,只是这个用Mockito写的测试仅仅对方法get()打桩一即使交互从未发生, 它也会成功通过。如果我们真的希望验证Translator使用Internet的行为,我们就得增加一个对Mockito API的调用来进行检查:
或者,如果感觉不必那么精确:
模拟对象库API通常是个人喜好问题。但是Mockito在测试风格上有一个明显的优势,那就是主要依赖于打桩一在你的特定 上下文中这可 能是优势,也可能不是。 测试代码每天都保持可读、简洁、可维护,这才是关键。这值得停下来权衡一下,明智地选择工具。 咱们再次借用J. B.的话来明确JMock与Mockito在方式和适用条件方面的区别: 当我想要拯救遗留代码时,我选择Mockito。当我想要设计新功能时,我选择JMock。 JMock与Mockito不同的前提假设,使得两者擅长不同的任务。默认情况下,想放宽这个假设,你就得增加一个stub。另一方面,Mockito 认为测试替身(也叫Mock)允许客户在任何时候调用任何方法。如果你想加强这个假设,那么你就得验证某个方法的调用。这就是区别所在。 不论你决定选择哪个库,我们的第三个即最后一个测试替身指南全都适用。 3.3.5注入依赖 为了能够使用测试替身,你需要一种替换 真实事物的方法。当涉及依赖时——为了测试目的而替换协作对象——我们的指南建议不要在同一个地方同时实例化和使用它们。在实践中,这意味着将这些对象另存为私有成员,或借助工厂方法来获取它们。 一旦你隔离开依赖, 你就需要访问它。你可以用可见性修饰符来破坏封装——将私有(private)内容变成公开(public) 或者包级私有(package private) ——或使用反射 API来将测试替身分配给私有字段。那种方式很快就会变得丑陋。更好的选择是采用依赖注入,从外部将依赖传递给对象,通常使用构造函数注人,正如在Translator例子中那样。 我们对于测试替身说得够多了,我渴望进行第二部分了,接下来对本章学到的东西做一个回顾吧。 3.4小结 我们从使用测试替身的理由开始,先探讨了测试替身的话题。这些原因背后的共同点是需要隔离被测代码,这样你就能模拟出所有场景,并且测试到代码应该表现出的所有行为。 有时,使用测试替身的理由是希望测试运行得更快。采用简单实现的测试替身,通常比它们替换掉的实现要快一到两倍。 有时,被测代码依赖于随机或其他不确定的行为,比如时间。这些情况下,测试替身通过将不可预测变得可预测而使测试变得简单。 对于模拟某些特殊情况并验证对象的预期行为,测试替身可能是仅有可行方式,而不必仅为了可测性而修改产品代码的设计或暴露细节。 除了测试替身带来的好处,我们继续学习了四种测试替身之间的区别:测试桩、伪造对象、测试间谍、模拟对象。极度简约的桩或许最适合切断不相关的协作者。当真实事物难以使用或比较麻烦时,伪造对象提供了闪电般的变换。测试间谍用来访问隐藏的信息和数据,而模拟对象像打了激素的测试间谍,增加了动态配置行为的能力,并验证预期的交互确实会发生。 我们从各种测试替身使用的时机和场景开始,以一些测试替身的基本使用指南来结束本章。尤其是,当使用模拟对象时,避免过紧地钉住实现是很重要的。你的Mock应当验证预期行为,而尽量放过行为的具体实现。 通常,工具可以提供极大的帮助,而且值得为你的特殊需要以及模拟对象的使用风格来评估最合适的库。最后,选择从外部注人依赖,而不是从被测代码内部硬连接它们,将会带来截然不同的可测性。 本章讲了测试替身,第一部分也到此结束。我们现在探讨了编写良好测试的基础。先是理解编写测试的好处,然后了解测试应该具有的属性,接下来操控程序员最基本的测试工具一测试替身一你现在掌握了足够的基本技能,你已经可以开始进一步磨炼了。 特别是,你已经准备好开始训练对各种不良测试的感觉,从表现良好到令人头痛,到地板上的臭袜子,甚至是彻头彻尾的维护负担。该是进人第二部分并关注坏味道的时候了。 那正是测试坏味道。 如果有对软件测试感兴趣的小伙伴可以加群了解更多:点击进群https://jq.qq.com/?_wv=1027&k=Co3BBZb7 |
|
开发测试 最新文章 |
pytest系列——allure之生成测试报告(Wind |
某大厂软件测试岗一面笔试题+二面问答题面试 |
iperf 学习笔记 |
关于Python中使用selenium八大定位方法 |
【软件测试】为什么提升不了?8年测试总结再 |
软件测试复习 |
PHP笔记-Smarty模板引擎的使用 |
C++Test使用入门 |
【Java】单元测试 |
Net core 3.x 获取客户端地址 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/18 5:50:22- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |