目录

一、Mock在单元测试中扮演一个什么角色

二、测试准备

三、使用Mock的理由

四、使用Python Mock

五、MagicMock类

六、mock.create_autospce

七、mock.patch和mock.patch.object

八、python3中的mock(前面介绍的是python2的)

1、简单的例子

2、完成功能测试

3、解决测试依赖


一、Mock在单元测试中扮演一个什么角色

  有时,你需要为单元测试的初始设置准备一些“其他”的代码资源。但这些资源兴许会不可用,不稳定,或者是使用起来太笨重。你可以试着找一些其他的资源替代;或者你可以通过创建一个被称为mock的东西来模拟它。Mocks能够让我们模拟那些在单元测试中不可用或太笨重的资源。
  
   在Python中创建mock是通过Mock模块完成的。你可以通过每次一个属性(one-attribute-at-a-time)或一个健全的字典对象或是一个类接口来创建mock。你还可以定义mock的行为并且在测试过程中检查它的使用。让我们继续探讨。

二、测试准备

Test Case Test Subject Test Resource

  典型的测试准备最少有两个部分。首先是测试对象(红色),这是测试的关注点。它可以是一个方法、模块或者类。它可以返回一个结果,也可以不返回结果,但是它可以根据数据数据或者内部状态产生错误或者异常。
  
  第二测试用例(灰色),它可以单独运行也可以作为套件的一部分。它是为测试对象准备的,也可以是测试对象需要的任意数据或资源。运行一个或多个测试事务,在每个测试中检查测试对象的行为。收集测试结果并用一个简洁、易读的格式呈现测试结果。
  
  现在,为了发挥作用,一些测试对象需要一个或多个资源(绿色)。这些资源可以是其他的类或者模块,甚至是一个非独立的进程。不论其性质,测试资源是功能性的代码。他们的角色是支持测试对象,但是他们不是测试的关注点。

三、使用Mock的理由

  但是有些时候,测试资源不可用,或者不适合。也许这个资源正在和测试对象并行开发中,或者并不完整或者是太不稳定以至于不可靠。
  
  测试资源太昂贵,如果测试资源是第三方的产品,其高昂的价格不适用于测试。测试资源的建立过于复杂,占用的硬件和时间可以用于别的地方。如果测试资源是一个数据源,建立它的数据集模仿真实世界是乏味的。
  
  测试资源是不可预知的。一个好的单元测试是可重复的,允许你分离和识别故障。但是测试资源可能给出随机的结果,或者它会有不同的响应时间。而作为这样的结果,测试资源最终可能成为一个潜在的搅局者。
  
   这些都是你可能想要用mock代替测试资源的原因。mock向测试对象提供一套和测试资源相同的方法接口。但是mock是更容易创建和管理。它能向测试对象提供和真实的测试资源相同的方法接口。它能提供确定的结果,并可以自定义以适用于特定的测试。能够容易的更新,以反映实际资源的变化。
  
  当然,mocks不是没有问题的。设计一个精确的mock是困难的,特别是如果你没有测试资源的可靠信息。你可以尝试找到一个开源的接口,或者你能对测试资源的方法接口进行猜测。无论你如何选择,你都可以在以后轻松的更新mock,你可以在首选资源中得到更详细的信息。
  
  太多的mock会使测试过于复杂,让你跟踪错误变得更困难。最好的实践是每个测试用例限制使用一到两个mock,或者为每个mock/对象对使用独立的测试用例。

四、使用Python Mock

  在Python中Mock模块是用来创建和管理mock对象的。该模块是Michael Foord的心血结晶,它是Python3.0的标准模块。因此在Python2.4~2.7中,你不得不自己安装这个模块。你可以 Python Package Index website从获得Mock模块最新的版本。
  Mock模块中有两个非常重要的类Mock、MagicMock和一个重要的方法create_autospec。

五、MagicMock类

MagicMock类是Mock类的子类,区别在于MagicMock类实现了常用的魔术方法,比如__str__、__iter__等,其他一样。

六、mock.create_autospce

  mock.create_autospec为类提供了一个同等功能实例。这意味着,实际上来说,在使用返回的实例进行交互的时候,如果使用了非法的方法将会引发异常。更具体地说,如果一个方法被调用时的参数数目不正确,将引发一个异常。这对于重构来说是非常重要。当一个库发生变化的时候,中断测试正是所期望的。如果不使用auto-spec,即使底层的实现已经破坏,我们的测试仍然会通过。
  在选择使用mock.Mock实例,mock.MagicMock实例或create_autospec方法的时候,通常倾向于选择使用 create_autospec方法,因为它能够对未来的变化保持测试的合理性。这是因为mock.Mock和mock.MagicMock会无视底层的API,接受所有的方法调用和参数赋值。

class Target(object):def apply(value):return valuedef method(target, value)return target.apply(value)

我们像下面这样使用mock.Mock实例来做测试:

class MethodTestCase(unittest.TestCase):def test_method(self):target = mock.Mock()method(target, 'value')target.apply.assert_called_with('value')

这个逻辑看似合理,但如果我们修改Target.apply方法接受更多参数:

class Target(object):def apply(value, are_you_sure):if are_you_sure:return valueelse:return None

重新运行你的测试,然后你会发现它仍然能够通过。这是因为它不是针对你的API创建的。这就是为什么你总是应该使用create_autospec方法,并且在使用@patch和@patch.object装饰方法时使用autospec参数。

七、mock.patch和mock.patch.object

1.参数

unittest.mock.patch(target,new = DEFAULT,spec = None,create = False,spec_set = None,autospec = None,new_callable = None,** kwargs )

  • target参数必须是一个str,格式为'package.module.ClassName',
    注意这里的格式一定要写对,如果你的函数或类写在pakege名称为a下,b.py脚本里,有个c的函数(或类),那这个参数就写“a.b.c”
  • new参数如果没写,默认指定的是MagicMock
  • spec=True或spec_set=True,这会导致patch传递给被模拟为spec / spec_set的对象
  • new_callable允许您指定将被调用以创建新对象的不同类或可调用对象。默认情况下MagicMock使用。

注意:@mock.patch整个对象,@mock.patch.object对象中的方法

示例

# 文件名:Mymodel
class MyTest(object):def func(self):pass

那么@mock.patch

import Mymodelclass AppMockTests(unit.TestCase):def setUp(self):super(AppMockTests, self).setUp()self.project_zbj = webtest.TestApp(self.loadapp('project_zbj'))@mock.patch('Mymodel.MyTest')  # 必须是字符串,具体到类def test_list(self, mock_MyTest):url = '/api/zzz'mock_MyTest.func.return_value = 500res = self.project_zbj.get(url)self.assertEqual(res.json['data'], 500)

而@mock.patch.object

import Mymodelclass AppMockTests(unit.TestCase):def setUp(self):super(AppMockTests, self).setUp()self.project_zbj = webtest.TestApp(self.loadapp('project_zbj'))@mock.patch.object(Mymodel.MyTest, 'func')  # 具体到某个方法 def test_list(self, mock_func):url = '/api/zzz'mock_func.return_value = 500res = self.project_zbj.get(url)self.assertEqual(res.json['data'], 500)

八、python3中的mock(前面介绍的是python2的)

  Mocks让我们为单元测试模拟了那些不可用或者是太庞大的资源。我们可以在运行中配置mock,在特定的测试中改变它的行为或响应,或者让它在恰当的时候抛出错误和异常。

 但在,实际生产中的项目是非常复杂的,对其进行单元测试的时候,会遇到以下问题:

  • 接口的依赖
  • 外部接口调用
  • 测试环境非常复杂

  单元测试应该只针对当前单元进行测试, 所有的内部或外部的依赖应该是稳定的, 已经在别处进行测试过的.使用mock 就可以对外部依赖组件实现进行模拟并且替换掉, 从而使得单元测试将焦点只放在当前的单元功能。

1、简单的例子

我们先从最简单例子开始。

modular.py

#modular.pyclass Count():def add(self):pass

这里要实现一个Count计算类,add() 方法要实现两数相加。但,这个功能我还没有完成。这时就可以借助mock对其进行测试。

mock_demo01.py

from unittest import mock
import unittestfrom modular import Count# test Count class
class TestCount(unittest.TestCase):def test_add(self):count = Count()count.add = mock.Mock(return_value=7)result = count.add(2,5)self.assertEqual(result,7)# 如果是python2,那么return_value的值会保存在返回值的json格式中的dataself.assertEqual(result.json['data'], 7)if __name__ == '__main__':unittest.main()

  count = Count()

  首先,调用被测试类Count() 。

  count.add = mock.Mock(return_value=7)

  通过Mock类模拟被调用的方法add()方法,return_value 定义add()方法的返回值。

  result = count.add(2,5)

  接下来,相当于在正常的调用add()方法,传两个参数2和5,然后会得到相加的结果7。然后,7的结果是我们在上一步就预先设定好的。

  self.assertEqual(result,7)

  最后,通过assertEqual()方法断言,返回的结果是否是预期的结果7。

  运行测试结果:

> python3 mock_demo01.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000sOK

这样一个用例就在mock的帮助下编写完成,并且测试通过了。

2、完成功能测试

再接下来完成module.py文件中add()方法。

# module.pyclass Count():def add(self, a, b):return a + b

然后,修改测试用例:

from unittest import mock
import unittest
from module import Countclass MockDemo(unittest.TestCase):def test_add(self):count = Count()count.add = mock.Mock(return_value=13, side_effect=count.add)result = count.add(8, 8)print(result)count.add.assert_called_with(8, 8)self.assertEqual(result, 16)if __name__ == '__main__':unittest.main()

 count.add = mock.Mock(return_value=13, side_effect=count.add)

  side_effect参数和return_value是相反的。它给mock分配了可替换的结果,覆盖了return_value。简单的说,一个模拟工厂调用将返回side_effect值,而不是return_value。

  所以,设置side_effect参数为Count类add()方法,那么return_value的作用失效。

  result = count.add(8, 8)

  print(result)

  这次将会真正的调用add()方法,得到的返回值为16(8+8)。通过print打印结果。

  assert_called_with(8,8)

  检查mock方法是否获得了正确的参数。

3、解决测试依赖

  前面的例子,只为了让大家对mock有个初步的印象。再接来,我们看看如何mock方法的依赖。

  例如,我们要测试A模块,然后A模块依赖于B模块的调用。但是,由于B模块的改变,导致了A模块返回结果的改变,从而使A模块的测试用例失败。其实,对于A模块,以及A模块的用例来说,并没有变化,不应该失败才对。

  这个时候就是mock发挥作用的时候了。通过mock模拟掉影响A模块的部分(B模块)。至于mock掉的部分(B模块)应该由其它用例来测试。

# function.py
def add_and_multiply(x, y):addition = x + ymultiple = multiply(x, y)return (addition, multiple)def multiply(x, y):return x * y

  然后,针对 add_and_multiply()函数编写测试用例。func_test.py

import unittest
import functionclass MyTestCase(unittest.TestCase):def test_add_and_multiply(self):x = 3y = 5addition, multiple = function.add_and_multiply(x, y)self.assertEqual(8, addition)self.assertEqual(15, multiple)if __name__ == "__main__":unittest.main()

运行结果:

>  python3 func_test.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000sOK

  目前运行一切正确常,然而,add_and_multiply()函数依赖了multiply()函数的返回值。如果这个时候修改multiply()函数的代码。

……
def multiply(x, y):return x * y + 3

  这个时候,multiply()函数返回的结果变成了x*y加3。

  再次运行测试:

>  python3 func_test.py
F
======================================================================
FAIL: test_add_and_multiply (__main__.MyTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):                                      File "fun_test.py", line 19, in test_add_and_multiply                 self.assertEqual(15, multiple)
AssertionError: 15 != 18                                                ----------------------------------------------------------------------
Ran 1 test in 0.000s                                                    FAILED (failures=1)   

  测试用例运行失败了,然而,add_and_multiply()函数以及它的测试用例并没有做任何修改,罪魁祸首是multiply()函数引起的,我们应该把 multiply()函数mock掉。

import unittest
from unittest.mock import patch
import functionclass MyTestCase(unittest.TestCase):@patch("function.multiply")def test_add_and_multiply2(self, mock_multiply):x = 3y = 5mock_multiply.return_value = 15addition, multiple = function.add_and_multiply(x, y)mock_multiply.assert_called_once_with(3, 5)self.assertEqual(8, addition)self.assertEqual(15, multiple)if __name__ == "__main__":unittest.main()

  @patch("function.multiply")

  patch()装饰/上下文管理器可以很容易地模拟类或对象在模块测试。在测试过程中,您指定的对象将被替换为一个模拟(或其他对象),并在测试结束时还原。

  这里模拟function.py文件中multiply()函数。

  def test_add_and_multiply2(self, mock_multiply):

  在定义测试用例中,将mock的multiply()函数(对象)重命名为 mock_multiply对象

  mock_multiply.return_value = 15

  设定mock_multiply对象的返回值为固定的15。

  ock_multiply.assert_called_once_with(3, 5)

  检查ock_multiply方法的参数是否正确。

  再次,运行测试用例,通过!

【全面解析Mock】Mock在单元测试中扮演一个什么角色?相关推荐

  1. Mock和Java单元测试中的Mock框架Mockito介绍

    什么是Mock? 在面向对象程序设计中,模拟对象(英语:mock object,也译作模仿对象)是以可控的方式模拟真实对象行为的假的对象.程序员通常创造模拟对象(mock object)来测试其他对象 ...

  2. 光复用技术中三种重要技术_传感器在机器人技术研究发展历程中扮演着重要角色...

    广义上来说,机器人包括一切模拟人类行为或思想以及模拟其他生物的机械,如机器狗.机器猫等.目前,智能机器人已成为世界各国的研究热点之一,成为衡量一国工业化水平的重要标志.相比之前机器人技术在人们眼中过于 ...

  3. 英语语音中的调核例子_英语歌曲在英语教学中扮演的重要角色

    每一种语言都是一门独到的艺术,而英语,它拥有"世界第一通用语言的称号",就足以证明它的价值有多高.当英语和音乐这两种截然不同的艺术融合在一起,另一种新的艺术诞生了,这就是英语歌曲. ...

  4. 博鳌直击 | 区块链在互联网金融中扮演怎样的角色?

    雷锋网3月24日报道,今日(3月24日),第16届博鳌亚洲论坛2017年年会在海南继续进行中.据雷锋网了解,在今日下午的数字货币与区块链分论坛上,中国银行前行长.中国互联网金融协会区块链工作组组长李礼 ...

  5. 为什么单片机需要时钟系统,时钟信号在单片机中扮演怎样的角色?

    现在想想,我自学单片机都快一年了.我自学过8051.msp430和STM32,做一些基本的小项目,参加过2018年江苏省电子设计大赛,还获得了一等奖. 我懂,也不懂单片机.不懂就要去查资料,然后弄懂, ...

  6. 元宇宙是什么,AR科技在元宇宙中扮演怎样的角色?

    元宇宙的兴起 不论在哪个领域工作,您都可能会接触到若干次"元宇宙"这一词语.实际上,近期围绕元宇宙的讨论热火朝天,许多人称之为互联网新领域及零售.消费者参与的新模式. 毋庸置疑,美 ...

  7. 冷链保温箱在冷链中扮演了什么角色?

    冷链运输是指某些食品原料.经过加工的食品或半成品.特殊的生物制品在经过收购.加工.灭活后,在产品加工.贮藏.运输.分销和零售.使用过程中,其各个环节始终处于产品所必需的特定低温环境下,减少损耗,防止污 ...

  8. 使用 Python Mock 类进行单元测试

    数据类型.模型或节点--这些都只是mock对象可承担的角色.但mock在单元测试中扮演一个什么角色呢? 有时,你需要为单元测试的初始设置准备一些"其他"的代码资源.但这些资源兴许会 ...

  9. 工业智能网关在物联网系统中扮演什么角色?

    工业智能网关在物联网系统中扮演的是怎样子的一个角色?工业智能网关在整套物联网的完整体系中担负的责任又有哪些呢?我们对工业智能网关的理解可能还只停留在边缘计算.数据采集.协议通信等表面层次上,但是这样子 ...

最新文章

  1. 剑指offer_第18题_二叉树的镜像_Python
  2. If one day
  3. Java7/8 中的 HashMap 和 ConcurrentHashMap
  4. Sql Server实用操作-动态sql语句基本语法
  5. ​凌云KTV点歌系统功能简介
  6. 网络防火墙单向和双向_单向晶闸管与双向晶闸管之间的不同之处
  7. 数学江湖中的“独孤九剑”
  8. libmysql.dll是否真的要拷贝到c:\windows目录下呢?
  9. ModuleNotFoundError: No module named ‘keras.api‘
  10. seo教程之对搜索引擎的研究
  11. java 快速排序算法简单_Java 快速排序算法的简单说明及实现
  12. fofa域名检测存活工具_检测cdn和寻找真实ip的一些方法总结
  13. 【西语】【6】el amor es 什么是爱
  14. Universally Slimmable Networks and Improved Training Techniques
  15. 仙武:开局神级召唤!(二)
  16. 程序人生 - 鼠标手是什么?如何应对鼠标手!
  17. 多视图多示例多标签的协同矩阵分解
  18. 安装配置Tomcat(CentOS7)
  19. nyoj 一笔画问题
  20. 机械键盘 单个按键不灵 修理

热门文章

  1. 模型过拟合原因及解决办法
  2. Linux(CentOS 7)
  3. 【智能制造】关于德国工业4.0的简要介绍
  4. 分治算法:根号n段合并排序算法
  5. 洛谷P3613 寄包柜
  6. eclipse android sdk开发环境搭建,Android SDK 2.3与Eclipse最新版开发环境搭建(四)
  7. android怎么把动图变成视频格式,Android 录制手机屏幕视频生成GIF图片实例详解
  8. 适合精致女孩使用的APP软件 不容错过的精彩人生
  9. 云南电视台短信互动平台软件方案
  10. 在VirtualBox上使用Bosh部署Cloud Foundry