测试人必会的Python内置库:unittest.mock(单元测试mock的基础使用)

这篇具有很好参考价值的文章主要介绍了测试人必会的Python内置库:unittest.mock(单元测试mock的基础使用)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 为什么需要使用mock

unittest.mock是用于在单元测试中模拟和替换指定的对象及行为,以便测试用例更加准确地进行测试运行。例如对于以下代码,想要针对函数func_a写一个简单的单元测试:

import unittest


def func_c(arg1, arg2):
    a_dict = {}
    # 其他代码
    return a_dict


def func_b(arg3, arg4):
    b_list = []
    a_arg1 = None
    a_arg2 = None
    # 其他代码
    a_dict = func_c(a_arg1, a_arg2)
    # 其他代码
    return b_list


def func_a():
    b_list = func_b('111', '222')
    if 'aaa' in b_list:
        return False

    return True


class FuncTest(unittest.TestCase):
    def test_func_a(self):
        assert func_a()

但是这样的话,函数func_b和func_c的逻辑都需要一起测试,在单元测试中这明显是不合理的,对于想要测试的函数func_a,里面所使用到的其他函数或接口,我们只需要关心它的返回值即可,保证当前测试的函数按它自己的逻辑运行,所以可以写成下面这样:

import unittest


def mock_func_b(arg3, arg4):
    return ['bbb', 'ccc']


def func_a():
    # 使用一个模拟的mock_func_b代替真正的函数func_b
    # 这个mock_func_b不需要关心具体实现逻辑,只关心返回值
    b_list = mock_func_b('111', '222')
    if 'aaa' in b_list:
        return False

    return True


class FuncTest(unittest.TestCase):
    def test_func_a(self):
        assert func_a()

注意,模拟的mock_func_b并不需要保证func_a中所有的可能分支和逻辑都执行一次,单元测试更多的是验证函数或接口(比如这里的func_a)是否与设计相符、发现代码实现与需求中存在的错误、修改代码时是否引入了新的错误等。但是这里的写法也有很大的问题,一个功能模块中使用的函数或接口通常来讲其实并不少、也没有这里这么简单,如果涉及的接口都要重新写一个mock对象(如mock_func_b),那单元测试的工作将会变得非常繁重和复杂,所以unittest中的mock模块派上了用场,这个模块也正如它的名称一样,可以模拟各种对象。

import unittest
from unittest import mock


def func_a():
    # 创建一个mock对象,return_value表示在该对象被执行时返回指定的值
    mock_func_b = mock.Mock(return_value=['bbb', 'ccc'])
    b_list = mock_func_b('111', '222')
    if 'aaa' in b_list:
        return False

    return True


class FuncTest(unittest.TestCase):
    def test_func_a(self):
        assert func_a()

2. Mock对象

2.1 快速上手

mock模块中的Mock类最常用的就是Mock和MagicMock,可以用来模拟对象、属性和方法,并且会保存这些被模拟的对象的使用细节,之后再使用断言来判断它们是否按照期待的被使用。

使用Mock类指定其被调用时触发的一些行为(Mock对象也可以用于替换指定的对象或方法)。

>>> from unittest.mock import MagicMock, Mock
>>> mock = Mock(side_effect=KeyError('foo'))
>>> mock()  # 直接调用将发生指定的异常
Traceback (most recent call last):
  ...
KeyError: 'foo'
>>> values = {'a': 1, 'b': 2, 'c': 3}
>>> def side_effect_func(arg):
...     return values[arg]
... 
>>> mock.side_effect = side_effect_func  # 重新指定side_effect
>>> mock('a'), mock('b'), mock('c')  # 表示只能传入指定的参数
(1, 2, 3)
>>> mock('a'), mock('b'), mock('c'), mock('d')  # 传入未指定的参数则会报错
Traceback (most recent call last):
  ...
KeyError: 'd'
>>> mock.side_effect = [5, 4, 3, 2, 1]  # 重新指定side_effect
>>> mock(), mock(), mock(), mock()  # 相当于迭代器,依次返回对应的值,使用完后再次调用就会报错
(5, 4, 3, 2)
>>> mock()
1
>>> mock()
Traceback (most recent call last):
  ...
StopIteration

使用spec参数指定Mock对象的属性和方法,指定时可以是一个对象,会自动将该对象的属性和方法赋给当前Mock对象,但是注意赋值的属性和方法也是Mock类型的,并不会真正执行对应方法的内容。

from unittest.mock import MagicMock, Mock


class SpecMock:
    def test_spec(self):
        print('spec running...')


def test_mock_spec():
    mock = Mock(spec=SpecMock())
    print(mock.test_spec)  # 注意打印的内容,返回的是一个Mock类型
    print(mock.test_spec())  # 该方法内的内容并没有被执行
    mock.func()


if __name__ == '__main__':
    test_mock_spec()

    
'''输出:
<Mock name='mock.test_spec' id='1956426692808'>
<Mock name='mock.test_spec()' id='1956430210952'>
Traceback (most recent call last):
  ...
AttributeError: Mock object has no attribute 'func'
'''

使用MagicMock创建并替换原有的方法。

from unittest.mock import MagicMock


class TestClass:
    def func(self, a, b):
        return a + b


tc = TestClass()
# 使用MagicMock创建并替换原来的func方法,并指定其被调用时的返回值
tc.func = MagicMock(return_value='666')
print(tc.func(2, 3))
# 判断func是否按照指定的方式被调用,如果没有,
# 比如这里指定assert_called_with(4, 5),就会抛出异常,
# 因为之前使用的是tc.func(2, 3)来进行调用的
print(tc.func.assert_called_with(2, 3))

'''输出:
666
None
'''

Mock类虽然支持对Python中所有的magic方法进行“mock”,并允许给magic方法赋予其他的函数或者Mock实例,但是如果需要使用到magic方法,最简单的方式是使用MagicMock类,它继承自Mock并实现了所有常用的magic方法。

>>> from unittest.mock import MagicMock, Mock, patch
>>> mock = Mock()
>>> mock.__str__ = Mock(return_value='666')
>>> str(mock)
'666'
>>> m_mock = MagicMock()
>>> m_mock.__str__.return_value = '999'
>>> str(m_mock)
'999'
>>> m_mock.__str__.assert_called_with()

可以使用create_autospec函数来创建所有和原对象一样的api。

>>> from unittest.mock import create_autospec
>>> def func(a, b, c):
...     pass
... 
>>> mock_func = create_autospec(func, return_value='func autospec...')
>>> func(1, 2, 3)
>>> mock_func(1, 2, 3)
'func autospec...'
>>> mock_func(111)
Traceback (most recent call last):
  ...
TypeError: missing a required argument: 'b'

2.2 Mock类和MagicMock类

Mock对象可以用来模拟对象、属性和方法,Mock对象也会记录自身被使用的过程,你可以通过相关assert方法来测试验证代码是否被执行过。MagicMock类是Mock类的一个子类,它实现了所有常用的magic方法。

2.2.1 Mock构造函数

构造函数 unittest.mock.Mock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs) 参数解释:

  • spec: 可以传入一个字符串列表、类或者实例,如果传入的是类或者实例对象,那么将会使用 dir 方法将该类或实例转化为一个字符串列表(magic属性和方法除外)。访问(get操作)任何不在此列表中的属性和方法时都会抛出AttributeError。如果传入的是一个类或者实例对象,那么__class__方法会返回对应的类,以便在使用 isinstance 方法时进行判断。
  • spec_set: spec参数的变体,但更加严格,如果试图使用get操作或set操作来操作此参数指定的对象中没有的属性或方法,则会抛出AttributeError。spec参数是可以对spec指定对象中没有的属性进行set操作的。参考 mock_add_spec 方法。
  • side_effect: 可以传入一个函数,每次当Mock对象被调用的时候,就会自动调用该函数,可以用于抛出异常或者动态改变mock对象的返回值,此函数使用的参数与mock对象被调用时传入的参数是一样的,并且,除非它的返回值为 unittest.mock.DEFAULT 对象,否则这个函数的返回值将会作为mock对象的返回值。也可以传入一个exception对象或者实例对象,如果传入exception对象,则每次调用mock对象都会抛出该异常。也可以传入一个可迭代对象,每次调用mock对象时就会返回该迭代对象的下一个值。如果不想使用了,可以将它设置为None。具体参见后面mock对象 side_effect 属性的使用。
  • return_value: 每次调用mock对象时的返回值,默认第一次调用时创建新的Mock对象。
  • unsafe: 如果某个属性或方法中会assert一个AttributeError,则可以设置 unsafe=True 来跳过这个异常。(Python3.5更新)
  • wraps: 包裹Mock对象的对象,当wraps不为None时,会将Mock对象的调用传入wraps对象中,并且可以通过Mock对象访问wraps对象中的属性。但是如果Mock对象指定了明确的return_value那么wraps对象就不会起作用了。
  • name: 指定mock对象的名称,可在debug的时候使用,并且可以“传播”到子类中。
  • 注: 初始化Mock对象时,还可以传入其他任意的关键字参数,这些参数会被用于设置成Mock对象的属性,具体参见后面的 configure_mock() 。

2.2.2 常用方法

assert_called()

assert:mock对象至少被调用过一次。(Python3.6新增)

assert_called_once()

assert:mock对象只被调用过一次。(Python3.6新增)

assert_called_with(*args, **kwargs)

assert:mock对象最后一次被调用的方式。

>>> from unittest.mock import Mock
>>> mock = Mock()
>>> mock.method(1, 2, 3, test='wow')
<Mock name='mock.method()' id='2956280756552'>
>>> mock.method.assert_called_with(1, 2, 3, test='wow')

assert_called_once_with(*args, **kwargs)

assert:mock对象以指定方式只被调用过一次。

assert_any_call(*args, **kwargs)

assert:mock对象以指定方式被调用过。

assert_has_calls(calls, any_order=False)

calls是一个 unittest.mock.call 对象列表,any_order默认为False,表示calls中的对象必须按照原来的调用顺序传入,为True则表示可以是任意文章来源地址https://www.toymoban.com/news/detail-428524.html

到了这里,关于测试人必会的Python内置库:unittest.mock(单元测试mock的基础使用)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Python初学者必会的11道测试题,含代码!!

    一个学妹问我的11道python题,我就顺手拿来做了下,发现有些题目还是很有意思的。 1 随机生成一个在[5,10]区间内的整数n,分别输出如下图所示的n行有规律字符图。 【提示】 (I)随机数生成需要导入random库后调用randint(a,b)函数; (2)利用序列range()函数,获得字母编码值,调用

    2024年02月07日
    浏览(60)
  • [Python]unittest-单元测试

    目录 unittest的大致构成: Test Fixture Test Case-测试用例 Test Suite-测试套件 Test Runner 批量执行脚本 makeSuite() TestLoader discover() 用例的执行顺序 忽略用例执行 skip skipIf skipUnless 断言 HTML测试报告 错误截图 unittest是python中的单元测试框架 大致作用: 提供用例的组织与执行-组织大量的测

    2024年02月08日
    浏览(32)
  • 系统学习Python——单元测试unittest:编写测试用例

    分类目录:《系统学习Python》总目录 相关文章: · 单元测试unittest:框架结构 · 单元测试unittest:测试固件 · 单元测试unittest:编写测试用例 · 单元测试unittest:执行测试用例 · 单元测试unittest:用例执行次序 · 单元测试unittest:内置装饰器 · 单元测试unittest:命令行执行测

    2023年04月08日
    浏览(52)
  • 系统学习Python——单元测试unittest:执行测试用例

    分类目录:《系统学习Python》总目录 相关文章: · 单元测试unittest:框架结构 · 单元测试unittest:测试固件 · 单元测试unittest:编写测试用例 · 单元测试unittest:执行测试用例 · 单元测试unittest:用例执行次序 · 单元测试unittest:内置装饰器 · 单元测试unittest:命令行执行测

    2024年02月06日
    浏览(38)
  • Python高级用法:使用unittest进行单元测试

    Python中含有一个单元测试模块unittest,我们可以使用其中的工具来测试代码。在编写测试用例后,可以查看一系列输入是否得到了预期的输出。单元测试专注于测试软件单元,软件单元可以被理解为应用程序代码的最小可测试部分。 在代码中我们需要测试的是一个数字是否为

    2024年01月19日
    浏览(50)
  • Python中的单元测试框架:使用unittest进行有效测试

    在软件开发中,单元测试是一种测试方法,它用于检查单个软件组件(例如函数或方法)的正确性。Python 提供了一个内置的单元测试库,名为 unittest ,可以用来编写测试代码,然后运行测试,并报告测试结果。 本文将向你介绍如何使用 unittest 来编写和运行单元测试。通过阅

    2024年03月09日
    浏览(40)
  • Python接口自动化之unittest单元测试

    以下主要介绍unittest特性、运行流程及实际案例。 一、单元测试三连问 1、 什么是单元测试?   按照阶段来分,一般就是单元测试,集成测试,系统测试,验收测试。单元测试是对 单个模块 、 单个类 或者 单个函数 进行测试。 将访问接口的过程封装在函数里面; 接口测试就

    2024年02月07日
    浏览(47)
  • python技术栈 之 单元测试中mock的使用

    一、什么是mock? mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。 二、mock的作用 特别是开发过程中上下游未完成的工序导致当前无法测试,需要虚拟某些特定对象以便测试。 unittest是python内置的单元测

    2024年02月13日
    浏览(35)
  • python技术栈之单元测试中mock的使用

    mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。 特别是开发过程中上下游未完成的工序导致当前无法测试,需要虚拟某些特定对象以便测试。 unittest是python内置的单元测试库,在做接口测试时,如果开

    2024年02月08日
    浏览(39)
  • 单元测试|unittest生成测试报告

    测试报告为测试结果的统计即展示,是自动化测试不可或缺的一部分,利用unittest 可以生成测试报告。 使用第三方 HTMLTestRunner 执行测试用例集,生成网页版测试报告 HTMLTestRunner是Python标准库的unittest模块的一个扩展,在使用该模块之前 要下载HTMLTestRunner.py文件,并将该文件保

    2024年02月09日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包