(以下代码可以在Jupyter中运行。) 我有一个B类,它使用A类,需要测试。
class A:
async def f(self):
pass
class B:
async def f(self):
a = A()
x = await a.f() # need to be patched/mocked
我有以下测试代码。看来它模拟了 A 的类方法而不是实例方法。
from asyncio import Future
from unittest.mock import MagicMock, Mock, patch
async def test():
sut = B()
with patch('__main__.A') as a: # it's __main__ in Jupyter
future = Future()
future.set_result('result')
a.f = MagicMock(return_value=future)
await sut.f()
await test()
但是,代码出现错误:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
C:\Users\X~1\AppData\Local\Temp\1/ipykernel_36576/3227724090.py in <module>
20 await sut.f()
21
---> 22 await test()
C:\Users\X~1\AppData\Local\Temp\1/ipykernel_36576/3227724090.py in test()
18 future.set_result('result')
19 a.f = MagicMock(return_value=future)
---> 20 await sut.f()
21
22 await test()
C:\Users\X~1\AppData\Local\Temp\1/ipykernel_36576/3227724090.py in f(self)
6 async def f(self):
7 a = A()
----> 8 x = await a.f() # need to be patched/mocked
9
10 from asyncio import Future
TypeError: object MagicMock can't be used in 'await' expression
在 Python 3.8+ 中,修补异步方法会为您提供 AsyncMock,因此提供结果更加简单。
在补丁方法本身的文档中:
如果省略 new,则如果修补的对象是异步函数,则目标将替换为 AsyncMock,否则目标将替换为 MagicMock。
AsyncMock 可以让您以更直接的方式提供返回值:
import asyncio
from unittest.mock import patch
class A:
async def f(self):
return "foo"
class B:
async def f(self):
return await A().f()
async def main():
print(await B().f())
with patch("__main__.A.f", return_value="bar") as p:
print(await B().f())
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
sys.exit(1)
....打印:
$ python example.py
foo
bar
side_effect
kwarg 涵盖了您想要返回的大多数类型的值(例如,如果您需要模拟函数等待某些内容)。
- 如果 side_effect 是一个函数,则异步函数将返回该函数的结果,
- 如果 side_effect 是异常,则异步函数将引发异常,
- 如果 side_effect 是一个可迭代对象,则异步函数将返回可迭代对象的下一个值,但是,如果结果序列已用完,则立即引发 StopAsyncIteration,
- 如果未定义 side_effect,则 async 函数将返回 return_value 定义的值,因此,默认情况下,async 函数返回一个新的 AsyncMock 对象。
需要改变
a.f = MagicMock(return_value=future)
到
a().f = MagicMock(return_value=future)
我面临类似的问题,我必须返回修补的对象函数调用的值。 例如,我必须模拟
OAuthClient
有一个异步函数
我使用
new_callable=AsyncMock
来修改 @patch
装饰器的默认行为
https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch:~:text=These%20will%20be%20passed%20to%20AsyncMock%20if%20the%20patched%20object %20 是%20asynchronous%2C%20to%20MagicMock%20otherwise%20or%20to%20new_callable%20if%20specified.
class AuthService:
def __init__(self) -> None:
self.auth_client = AuthClient()
async def get_token(payload):
return await self.auth_client.get_token(payload)
class AuthServiceTest:
@pytest.mark.usefixtures('auth_token_response')
@patch("app.client.AuthClient.__init__", return_value=None, new_callable=AsyncMock)
async def test_get_token(auth_client_async_mock, auth_token_response):
auth_client_async_mock.get_token.return_value = auth_token_response
service = AuthService(auth_client = auth_client_async_mock)
response = service.get_token(
payload={"username": "alex", "password": "strong_password", "grant_type": "password"}
)
# assert response