pytest-mock 数据模拟

文章目录

mock 测试

在单元测试时,有些数据需要依赖其他服务或者不好获取到,此时需要使用mock来模拟对应的函数、对象等。

mock模拟数据的python框架:
unittest.mock, 标准模块;,有基于此的mock扩展包;
faker 生成假数据
pytest-mock, 扩展模块

mock 作用:

  • 解决web开发中,后端接口不存在时,使用mock模拟数据返回;
  • 依赖第三方接口时, mock模拟返回;
  • demo 演示效果;

unittest.mock

Mock类

  • 核心类Mock,基类;
  • 可以创建属性、方法,并存储如何被调用;
  • Mock实例化的参数
    • spec, 字符串列表或者对象(dir获取对象的属性名称),表示该Mock对象可以访问的属性;访问不在列表中的属性时,报错AttributeError;
    • spec_set,属于spec的严格变体,使用方法类似;
    • side_effect, 调用Mock对象时,执行的函数,常用引发异常,或者动态改变函数的返回值; 如果是可迭代对象,则每次调用Mock对象都获取一个值;
    • return_value, 指定调用Mock对象的返回值;
    • wraps,包裹的函数,Mock对象的调用,实际是包裹函数的调用,并返回,类似side_effect;
    • name, Mock对象的名称;
  • 使用方法:
python 复制代码
from unittest.mock import Mock
class Lauf:
    def __init__(self, name, age):
    	self.name = name
        self.age = age

# 创建对象
lauf = Lauf("jack", 25)
mock_obj = Mock(spec=lauf) # mock对象具有lauf的属性、方法,可以进行属性赋值
mock_obj.name = "lili"

# side_effect 用于引发异常
mock = Mock(side_effect=KeyError('foo'))
mock()
# 抛出异常
KeyError: 'foo'
mock.side_effect = 可重新赋值

# side_effect 用于执行函数动作
m4 = Mock(side_effect=lambda: print("执行函数"))
m4()
执行函数
m4.mock_calls # 查看调用记录


# return_value 直接指定返回值
In [58]: m5 = Mock(return_value="jack")
In [59]: m5()
Out[59]: 'jack'

# wraps 包裹
In [67]: def func():
    ...:     print("func is running...")
    ...: 

In [68]: m8 = Mock(wraps=func)
In [69]: m8()
func is running...

MagicMock类

  • MagicMock是 Mock的子类,默认实现了大部分魔法方法;
  • 简单使用
python 复制代码
from unittest.mock import MagicMock


class Lauf:
    def __init__(self, name, age):
    	self.name = name
        self.age = age

# 模拟方法
lauf.run_method = MagicMock(return_value="running...")

lauf.run_method(3,4,5,key="value") # 参数随意
'running...'

# 断言
lauf.run_method.assert_called_once_with(3,4,5,key="value") # 带着这些参数被调用一次

patch装饰器

  • 模拟xxx,得到一个MagicMock对象;
  • 使用:
python 复制代码
from unittest.mock import patch

In [83]: @patch("os.path")
    ...: def func(a): # 模拟os.path得到一个MagicMock对象,传给函数
    ...:     print(a, a is os.path)
    ...: 
    ...: 

In [84]: func()
<MagicMock name='path' id='1620313178896'> True


# 依次模拟,得到多个MagicMock对象
@patch("requests.post")
@patch("requests.get")
def func(get_mock, post_mock):
	print(get_mock is requests.get) # True
	print(post_mock is requests.post) # True


# 模拟类的对象
# 为类的对象的属性、方法(必须该类中存在) 创建一个MagicMock对象
with patch.object(Lauf, 'method', return_value=None) as mock_method:
    lauf = Lauf('a', 10)
    lauf.method(1, 2, 3)

# 断言
mock_method.assert_called_once_with(1, 2, 3)


# 上下文内有效
foo = {'key': 'value'}
original = foo.copy()
with patch.dict(foo, {'newkey': 'newvalue'}, clear=True):
    assert foo == {'newkey': 'newvalue'} # 测试时foo变为新字典

# 测试结束,foo恢复
assert foo == original


# 操作魔法方法
m = MagicMock()
m.__str__.return_value = "jack"
str(m) # 返回"jack"

m.__str__ = MagicMock(return_value="xxx")

create_autospec函数

  • 创建mock对象,并确保与模拟的函数、对象具有相同的接口;
  • patch(autospec=True)
python 复制代码
In [98]: def func(a,b,c):
    ...:     print(a,b,c)
    ...: 

# 模拟函数,并确保参数相同
In [99]: mock_func = create_autospec(func, return_value="3")

In [100]: mock_func(1,2,3)
Out[100]: '3'


# 模拟对象,并确保相同的接口
In [106]: mock_obj = create_autospec(Lauf("jack", 23))

In [107]: mock_obj.name
Out[107]: <NonCallableMagicMock name='mock.name' spec='str' id='1620277710272'>
mock_obj.name = "lili" # 赋值

In [108]: mock_obj.age
Out[108]: <NonCallableMagicMock name='mock.age' spec='int' id='1620302996240'>

断言的方法

  • assert_called()
    Assert that the mock was called at least once.
python 复制代码
mock = Mock()
# 调用mock
mock()
# 断言
mock.assert_called()

# 返回一个新的Mock对象
mock.method()
<Mock name='mock.method()' id='...'>
# mock.xx 随即返回一个新的 mock对象,新的mock对象断言
mock.method .assert_called()
  • assert_called_once()
    Assert that the mock was called exactly once.
python 复制代码
mock = Mock()
mock.assert_called_once()  # 仅仅调用一次,多/没调用  均异常
  • assert_called_with(*args, **kwargs)
    This method is a convenient way of asserting that the last call has been made in a particular way:
python 复制代码
mock = Mock()
mock.method(1, 2, 3, test='wow')
mock.method.assert_called_with(1, 2, 3, test='wow')
  • assert_called_once_with(*args, **kwargs)
    Assert that the mock was called exactly once and that call was with the specified arguments.
python 复制代码
mock = Mock(return_value=None)
mock('foo', bar='baz')
mock.assert_called_once_with('foo', bar='baz')

mock('other', bar='values')
mock.assert_called_once_with('other', bar='values')
Traceback (most recent call last):
  ...
AssertionError: Expected 'mock' to be called once. Called 2 times.
  • assert_any_call(*args, **kwargs),
    assert the mock has been called with the specified arguments.
python 复制代码
mock = Mock(return_value=None)
mock(1, 2, arg='thing')
mock('some', 'thing', 'else')
mock.assert_any_call(1, 2, arg='thing')

pytest-mock 使用

  • 安装
bash 复制代码
pip install pytest pytest-mock
  • 使用
相关推荐
FINE!(正在努力!)1 天前
PyTest框架学习
学习·pytest
程序员杰哥2 天前
接口自动化测试之pytest 运行方式及前置后置封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
测试老哥2 天前
Pytest+Selenium UI自动化测试实战实例
自动化测试·软件测试·python·selenium·测试工具·ui·pytest
水银嘻嘻2 天前
07 APP 自动化- appium+pytest+allure框架封装
python·appium·自动化·pytest
天才测试猿3 天前
接口自动化测试之pytest接口关联框架封装
自动化测试·软件测试·python·测试工具·职场和发展·测试用例·pytest
not coder4 天前
Pytest Fixture 详解
数据库·pytest
not coder4 天前
pytest 常见问题解答 (FAQ)
开发语言·python·pytest
程序员的世界你不懂4 天前
(1)pytest简介和环境准备
pytest
not coder4 天前
Pytest Fixture 是什么?
数据库·oracle·pytest
Tester_孙大壮4 天前
pytest中的元类思想与实战应用
pytest