Pytest 进阶学习之 Mock

栏目: 编程工具 · 发布时间: 5年前

内容简介:在做单元测试时,被测试函数有时并不是一个可执行的独立单元。被测试函数依赖于一些外部资源,比如另外一个函数的返回值、数据库中某一条数据值等。为了屏蔽外部依赖的干扰,我们会采用 Mock 技术。通过模拟测试资源的方式,满足依赖条件。从设计模式的角度看,对于满足单一职责原则的函数、类,使用 Mock 的方式忽略外部依赖进行测试也是合理的。因为,外部依赖的测试,应该在其内部完成,而不应转移到调用方。

在做单元测试时,被测试函数有时并不是一个可执行的独立单元。被测试函数依赖于一些外部资源,比如另外一个函数的返回值、数据库中某一条数据值等。

为了屏蔽外部依赖的干扰,我们会采用 Mock 技术。通过模拟测试资源的方式,满足依赖条件。

设计模式 的角度看,对于满足单一职责原则的函数、类,使用 Mock 的方式忽略外部依赖进行测试也是合理的。因为,外部依赖的测试,应该在其内部完成,而不应转移到调用方。

反推,如果单元测试不好写,那么很有可能是软件没有遵循一定的设计模式进行实践。这种情况下,最重要的是让软件符合设计模式的规范,而不要急于做单元测试。

2. Stub、Fake、Mock 区别

实际上,Mock 并非模拟测试资源的唯一方式,类似的还有 Stub 和 Fake。

Stub 为测试对象提供了一套方法接口,模拟真实的测试资源。当测试对象调用 Stub 方法时,Stub 响应预定的结果,也可能产生预定的错误或异常。Stub 可以跟踪和测试对象的交互,但不处理出入的数据。

Fake 也提供了一套方法接口,用于跟踪和测试对象的交互,但与 Stub 不同,Fake 处理了输入的数据,并以此产生结果。

使用 Stub 和 Fake ,都可以对测试对象进行状态验证。但是如果,想知道测试对象是否按照正确顺序调用了方法,则需要进行行为验证。

Mock 可以用于测试对象的行为验证。

3. Python 中的 Mock

Python 3.3 版本之前,使用 Mock 需要先从第三方安装:

$ pip install mock

自 Python 3.3 开始,Mock 被引入标准库中,命名为 unittest.mock。

先来看一个简单的例子(下面均以 Python 2.7 为例):

from mock import MagicMock, patch

m = MagicMock()
# mock test 函数,返回值 1
m.test = MagicMock(return_value=1)
# 调用 test 函数
m.test()
# 输出:1
class MyObj(object):
    value = 'old_value'

my = MyObj()
with patch.object(my, 'value', 'new_value'):
    print my.value
# 输出:new_value

使用 Mock 做测试的思路:

  1. 找到被测对象的外部依赖。可能是一个函数、对象、类等。
  2. 实例化 Mock 类,得到 Mock 对象,设置 Mock 对象的行为与依赖一致。比如,返回值、对象值等。
  3. 使用 Mock 对象替换掉外部依赖。
  4. 写测试代码和判断是否符合预期的断言。

4. Mock、MagicMock、patch 使用

4.1 Mock

Mock 类定义:

class unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)

重要参数:

  • return_value,表示当 mock 对象被调用,side_effect 函数返回的是 DEFAULT 时,返回值。
  • side_effect,这个参数指向一个可调用或可迭代的对象。当 mock 对象被调用时,如果该函数返回值不是 DEFAULT 时,那么以该函数的返回值作为 mock 对象调用的返回值

side_effect 有三种用法:

1,按序列返回值

m = Mock()
m.get_num = Mock(side_effect=[1, 2, 3])
p.get_num()
# 输出:1
p.get_num()
# 输出:2
p.get_num()
# 输出:3

2,调用函数处理返回值

m = Mock()
m.get_x = Mock(side_effect=lambda x: x*2)
m.get_x('123')
# 输出:'123123'

3,主动抛出异常

m = Mock()
m.get_exp = Mock(side_effect=TypeError('type error msg'))
m.get_exp()
# 输出:TypeError: type error msg

4.2 MagicMock

MagicMock 类定义:

class unittest.mock.MagicMock(MagicMixin, Mock)

MagicMock 是 Mock 类的一个子类,实现了很多 magic 方法和属性。

看一个例子:

m = Mock()
list(m)
# 输出: 'Mock' object is not iterable
mg = MagicMock()
list(mg)
# 输出:[]

创建 Mock 对象时,默认没有实现 __iter__ 函数,所以会报错。但是 ,MagicMock 对象中增加了这个 magic method。

通常情况下,除非是对迭代对象的 Mock,我们感受不到 Mock 和 MagicMock 的区别。

4.3 patch

patch 是 Mock 提供的一个用于替换某些函数、属性的方法。

patch 定义:

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

重要参数:

  • target,被替换的函数字符串名,需要输入完整路径
  • new,替换成的 Mock 实例,默认为 MagicMock

通常有两种写法:

  1. 使用 with 控制上下文范围,进行替换
import requests
from mock import patch

def request_get(url):
    return {'status':200}
with patch('requests.get', request_get):
    print requests.get('https://api.com')
# 输出:{'status': 200}
  1. 使用装饰器,替换指定的属性或函数
import requests
from mock import patch

@patch('requests.get', request_get)
def get_data():
    return requests.get('https://api.com')

get_data()
# 输出:{'status': 200}

patch 还有三个非常有用的拓展:

  1. 替换字典用的 patch.dict
    patch.dict(foo, {'newkey': 'newvalue'})
  2. 多次替换用的 patch.multiple
    @patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
  3. 替换实例用的 patch.object
    @patch.object(SomeClass, 'class_method')

5. 断言

断言,用于判断测试是否符合预期。Mock 相关的断言:

  • assert_not_called,没调用过
  • assert_called_with,调用过
  • assert_called_once_with,仅调用过一次
  • aseert_has_calls,按指定顺序调用过
  • assert_any_calls,是否全局调用过
  • reset_mock,重置调用记录

使用示例:

m = Mock()
m.get_a.assert_called_once()
# 输出:AssertionError: Expected 'get_a' to have been called once. Called 0 times.

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

美铁之战

美铁之战

[英]帕特里克·蒂利 / 黑曜、超侠 / 百花文艺出版社 / 2018-9 / 44.80元

本书的故事发生在未来,一场核战毁灭了北美大陆上的人类文明,残存下来的人类分化成两拨:生活在地面上退化到刀耕火种时代的平原人;躲藏在地下苟延残喘的沙穴人。几百年后,当保留着战前文明的沙穴人尝试着登上地面,和平原人的同室操戈将不可避免地上演……一起来看看 《美铁之战》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具