自动创建mock的pytest插件
项目描述
pytest-automock
原因
- 没有通用的automock解决方案
功能
- pytest插件
- 自动生成/自动使用函数和对象的mock
- 同步和异步支持
- 锁定模式以确保mock对象保持未更改
- 可定制的序列化
限制
- 不支持dunder方法(未来可能部分解决)
- 不支持同步/异步生成器/上下文
- 由于顺序很重要,竞争条件可能导致测试失败
- 非确定性的表示会破坏测试,因为表示是调用快照键的一部分
许可证
pytest-automock
在MIT许可证下提供。
需求
- python 3.6+
用法
假设你有一个模块mymod.py
import time
class Network:
def get_data_from_network(self, x, y):
time.sleep(1)
return x + y
def send_data_to_network(self, value):
time.sleep(1)
def logic(x):
n = Network()
a, b = 0, 1
while b < x:
c = n.get_data_from_network(a, b)
a, b = b, c
n.send_data_to_network("ok")
return b
并且你想要为你的Network
类创建mock(由于测试时间和合理的计数),但你太懒惰了,不想写它们... conftest.py
import pytest
import mymod
@pytest.fixture(autouse=True)
def _mocks(automock):
with automock((mymod, "Network")):
yield
你也可以使用模块路径表示法
import pytest
@pytest.fixture(autouse=True)
def _mocks(automock):
with automock("mymod.Network"):
yield
test_logic.py
:
from mymod import logic
def test_logic():
assert logic(7) == 8
assert logic(10) == 13
如果你在这个设置上运行pytest
,那么你会看到失败
$ pytest -x
...
E RuntimeError: Mock is locked, but '__init__' wanted
automock
可以在两种模式下工作:锁定和解锁。锁定模式是默认模式,不允许在此模式下调用mock对象的实际方法。所以,上面的错误表示我们不能调用我们的Network
的__init__
。在锁定模式下也没有mock文件的更新。
为了允许实际调用和mock生成,automock
为pytest
提供了额外的命令行参数:--automock-unlocked
$ pytest -x --automock-unlocked
...
test_logic.py .
...
1 passed in 22.09s
之后,你可以看到tests/mocks/test_logic/mymod/Network
文件已被创建。这是你的测试序列的mock。现在你可以重新运行测试并查看发生了什么(你可以省略--automock-unlocked
键以确保真实对象不会被触摸(实际上甚至创建))。
$ pytest -x
...
test_logic.py .
...
1 passed in 0.04s
API
automock
(.fixture)
automock
fixture是一个上下文管理器
def automock(*targets,
storage: Union[str, Path] = "tests/mocks",
override_name: Optional[str] = None,
unlocked: Optional[bool] = None,
remove: Optional[bool] = None,
encode: Callable[[Any], bytes] = default_encode,
decode: Callable[[bytes], Any] = default_decode,
debug: Optional[Callable[[Dict, Call, Optional[Call]], None]] = None)
*targets
:对象/模块和属性名(str
)或带有点分隔符的对象/函数的模块路径((mymod, "Network")
或"mymod.Network"
)的配对/元组storage
:存储mock的根路径override_name
:强制mock文件名unlocked
:模式选择器(如果省略,则由--automock-unlocked
选择)remove
:在测试运行前移除测试模拟(如果省略,则由--automock-remove
选择)encode
:编码程序decode
:解码程序debug
:调试失败用例的函数,当你不明白为什么automock无法进行时。参数包括memory
:当前失败的测试call_wanted
:你想要立即进行的调用call_saved
:你上一次为这个测试生成模拟时保存的调用
call_wanted
和call_saved
是丰富的Call
类对象,可以在mock.py
文件中检查。你也可以使用一个"pdb"
字符串代替你自己的函数作为debug
参数值,以使用带有pdb.set_trace()
指令的内部函数。默认的encode
/decode
程序使用pickle
和gzip
。
automock_unlocked
(固定装置)
从cli参数获取默认模式的固定装置(布尔值)。
automock_remove
(固定装置)
从cli参数获取默认模式的固定装置(布尔值)。
automock
(函数)
automock
函数不应由除automock
固定装置以外的任何人使用
def automock(factory: Callable, *,
memory: Dict,
locked: bool = True,
encode: Callable[[Any], bytes] = default_encode,
decode: Callable[[bytes], Any] = default_decode,
debug: Optional[Callable[[Dict, Call, Optional[Call]], None]] = None):
factory
:要包装的对象/函数memory
:获取/放置模拟的字典locked
:模式选择器encode
:编码程序decode
:解码程序debug
:与固定装置相同
默认的encode
/decode
程序使用pickle
和gzip
。
注意事项
顺序
正如功能段落所描述的:“顺序计数”。这是什么意思?
函数
模拟函数/协程调用顺序计数。如果你模拟序列
func(1, 2)
func(2, 3)
并尝试使用模拟数据与序列
func(2, 3)
func(1, 2)
你会得到一个错误,因为调用顺序是确定测试想法的一部分
对象
模拟对象具有相同的行为,但方法调用是单独的,所以如果你模拟序列
t1 = T(1)
t2 = T(2)
t1.func(1, 2)
t2.func(2, 3)
则方法调用顺序是单独的,所以这是可以的
t1 = T(1)
t2 = T(2)
t2.func(2, 3)
t1.func(1, 2)
但不是对于__init__
方法,因为模拟是内部附加到实例的
t2 = T(2)
t1 = T(1)
t1.func(1, 2)
t2.func(2, 3)
将会失败
函数参数
内部,模拟的关键是由实例编号和调用编号组成的。这导致一些“不明显”的行为
import time
from pytest_automock import automock
def nop(x):
return x
m = {}
mocked = automock(nop, memory=m, locked=False)
mocked(time.time())
mocked = automock(nop, memory=m, locked=True)
mocked(time.time())
将会因为模拟创建时的参数与模拟使用时的参数不同而失败。同样,如果pickle表示不是确定的,这也会破坏模拟。
开发
运行测试
由于覆盖率问题/功能,插件覆盖率默认已损坏。解决方案
COV_CORE_SOURCE=pytest_automock COV_CORE_CONFIG=.coveragerc COV_CORE_DATAFILE=.coverage.eager pytest
pytest-automock-0.9.1.tar.gz的散列
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 1fc66d4b96109d41291aa5bde6769fbacb18d21a22df52b1fbe6b25e988d8e0b |
|
MD5 | 796c474b0628dd841c9d0e45f0e3d8d0 |
|
BLAKE2b-256 | 47c16aa38070b150348c3e3491be15c17d0c906f11314f1442a534b42f96db51 |
pytest_automock-0.9.1-py3-none-any.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 48fe58111ec9870de8c7aa35699f0b102fd0a2fa290ee52a6fa73834b7d1e064 |
|
MD5 | 79817d8af5ef7527142cb0bdbe3e1ac9 |
|
BLAKE2b-256 | 987c7a4704084aeba91a7fdf58119de4385688171da1b817fff8b0b27ec40e73 |