我正在尝试测试应用程序是否正在重试.
@celery.task(bind=False, default_retry_delay=30)
def convert_video(gif_url, webhook):
// doing something
VideoManager().convert(gif_url)
return
except Exception as exc:
raise convert_video.retry(exc=exc)
Run Code Online (Sandbox Code Playgroud)
我正在嘲笑这个测试
@patch('src.video_manager.VideoManager.convert')
@patch('requests.post')
def test_retry_failed_task(self, mock_video_manager, mock_requests):
mock_video_manager.return_value= {'webm':'file.webm', 'mp4':'file.mp4', 'ogv' : 'file.ogv', 'snapshot':'snapshot.png'}
mock_video_manager.side_effect = Exception('some error')
server.convert_video.retry = MagicMock()
server.convert_video('gif_url', 'http://www.company.com/webhook?attachment_id=1234')
server.convert_video.retry.assert_called_with(ANY)
Run Code Online (Sandbox Code Playgroud)
我收到了这个错误
TypeError:exception必须是旧式类或派生自BaseException,而不是MagicMock
这是显而易见的,但我不知道该怎么做,否则测试是否正在调用该方法.
我有一个从PyPI导入模块的应用程序.我想为该应用程序的源代码编写单元测试,但我不想在这些测试中使用PyPI中的模块.
我想完全模拟它(测试机器不会包含那个PyPI模块,所以任何导入都会失败).
目前,每次我尝试加载我想在单元测试中测试的类时,我都会立即收到导入错误.所以我想也许用
try:
except ImportError:
Run Code Online (Sandbox Code Playgroud)
并捕获该导入错误,然后使用command_module.run().这看起来非常危险/丑陋,我想知道是否还有另一种方式.
另一个想法是编写一个适配器来包装PyPI模块,但我仍在努力.
如果你知道我可以模拟整个python包,我会非常感激.谢谢.
让我们假设我们有一个仅在生产阶段存在的模块系统.在测试时,这些模块不存在.但我仍然想为使用这些模块的代码编写测试.我们还假设我知道如何从这些模块中模拟所有必需的对象.问题是:如何方便地将模块存根添加到当前层次结构中?
这是一个小例子.我想测试的功能放在一个名为的文件中actual.py:
actual.py:
def coolfunc():
from level1.level2.level3_1 import thing1
from level1.level2.level3_2 import thing2
do_something(thing1)
do_something_else(thing2)
Run Code Online (Sandbox Code Playgroud)
在我的测试套件中,我已经拥有了我需要的一切:我拥有thing1_mock和thing2_mock.我也有测试功能.我需要的是添加level1.level2...到当前模块系统中.像这样:
tests.py
import sys
import actual
class SomeTestCase(TestCase):
thing1_mock = mock1()
thing2_mock = mock2()
def setUp(self):
sys.modules['level1'] = what should I do here?
@patch('level1.level2.level3_1.thing1', thing1_mock)
@patch('level1.level2.level3_1.thing1', thing2_mock)
def test_some_case(self):
actual.coolfunc()
Run Code Online (Sandbox Code Playgroud)
我知道我可以替换sys.modules['level1']包含另一个对象的对象,依此类推.但对我来说似乎有很多代码.我认为必须有更简单,更漂亮的解决方案.我找不到它.
我有一个看起来像这样的循环:
for i in range(len(some_list)):
response = requests.post(some_url, some_params)
if response.status_code != HTTPOk:
# do something
Run Code Online (Sandbox Code Playgroud)
我想做的是在循环的第二次迭代中更改requests.post的响应.从我的测试中,我知道我可以做以下事情:
mock_response = mock.Mock()
mock_response.status_code = 404
with mock.patch(mymodule.requests.post, return_value=mock_response):
mymodule.some_function()
Run Code Online (Sandbox Code Playgroud)
但这似乎只适用于一个status_code.我看了side_effect,看起来我可以像这样遍历循环:
mock_response.side_effect = [
mock.Mock(status_code=400), mock.Mock(status_code=200)
]
with mock.patch(mymodule.requests.post, return_value=mock_response):
mymodule.some_function()
Run Code Online (Sandbox Code Playgroud)
但是,看起来它实际上并没有获得"正确"的状态代码.更改side_effect或return_value中的行为以正确获取我想要的行为的最佳方法是什么?我认为side_effect就是我想要的,但我不确定最好的方法是嘲笑回应.
我编写了send_formatted_email格式化电子邮件主题和消息的send_email函数,然后在单独的模块中调用该函数。
现在我要测试send_formatted_email的调用send_email与预期的参数。为此,我试图模拟send_emailusing patch,但它并没有被模拟。
测试文件
@patch('app.util.send_email')
def test_send_formatted_email(self, mock_send_email):
mock_send_email.return_value = True
response = send_formatted_email(self.comment, to_email)
mock_send_email.call_args_list
....
Run Code Online (Sandbox Code Playgroud)
视图.py
def send_formatted_email(comment, to_email):
...
message = comment.comment
subject = 'Comment posted'
from_email = comment.user.email
...
return send_email(subject, message, to_email, from_email)
Run Code Online (Sandbox Code Playgroud)
实用程序
def send_email(subject, message, to, from):
return requests.post(
...
)
Run Code Online (Sandbox Code Playgroud)
我什至尝试过,app.util.send_email = MagicMock(return_value=True)但这也不起作用。知道我做错了什么吗?
我想要什么:
确保Foo在with语句中创建的所有实例都foo通过wraps=Foo.foo. 我想要这个的原因是我可以跟踪创建的所有实例call_count的方法。现在我这么说似乎有点不可能......fooFoo
>>> from mock import patch
...
... class Foo(object):
...
... def foo(self):
... return "foo"
...
... with patch("__main__.Foo.foo", wraps=Foo.foo) as m:
... foo = Foo()
... print(foo.foo())
Traceback (most recent call last):
File "a.py", line 12, in <module>
print(foo.foo())
File "/disk/software/lib/python27/mock/mock.py", line 1062, in __call__
return _mock_self._mock_call(*args, **kwargs)
File "/disk/software/lib/python27/mock/mock.py", line 1132, in _mock_call
return self._mock_wraps(*args, **kwargs)
TypeError: unbound method foo() must be …Run Code Online (Sandbox Code Playgroud) 我在测试中使用patchfromunittest.mock来更改远程 API 调用的行为。
我有三个不同的函数,它们返回三个不同的json文件,这些文件代表要从 API 返回的模拟数据。对于每个模拟 api 调用,我将其设置side_effect为这些函数之一。这种模式不是 DRY,但我不知道如何将参数传递给函数side_effect。
三个模拟 api 调用函数如下所示:
def mock_api_call_1():
with open('path/to/mock_1.json') as f:
mock_data = json.load(f)
return mock_data
Run Code Online (Sandbox Code Playgroud)
这是我的测试
class MyTest(TestCase):
def test_api(self):
with patch('mymodule.utils.api_call', side_effect=mock_api_call_1):
do_crud_operations()
self.assertEqual(MyModel.objects.all().count(), 10)
with patch('mymodule.utils.api_call', side_effect=mock_api_call_2):
do_crud_operations()
self.assertEqual(MyModel.objects.all().count(), 11)
Run Code Online (Sandbox Code Playgroud)
如何重构此代码以便能够将参数传递给side_effect(mock_call(1)而不是mock_call_1)。
从单元测试文档中,我看到:
side_effect:每当调用 Mock 时都会调用的函数。请参阅 side_effect 属性。对于引发异常或动态更改返回值很有用。使用与模拟相同的参数调用该函数,除非它返回 DEFAULT,否则该函数的返回值将用作返回值。
我看到传递给的函数采用side_effect与模拟相同的参数,但我仍然不确定如何最好地使用模拟来完成此任务。我最终会想要添加更多的测试用例,所以我不想对不同的mock_api_call函数进行硬编码。
在测试该create_response方法时,我似乎无法模拟该方法的返回值get_external_response。
/foo/响应
from abc import ABCMeta, abstractmethod
def create_response(url, type):
query = create_query(url, type)
external_response = get_external_response(query) <-- what I want to mock
return external_response
def create_query(url, type):
cc = MyFactory
return cc.get_concrete_class(url, type)
def get_external_response(cc):
return cc.make_query()
class MyAbstractClass(metaclass=ABCMeta):
def __init__(self, url, type):
self.url = url
self.type = type
self.query = self.make_query()
@abstractmethod
def make_query(self):
pass
class MyFactory:
@staticmethod
def get_concrete_class(url, type):
if type == 'A':
return MyClass(url, type)
else:
print("not valid type")
class MyClass(MyAbstractClass): …Run Code Online (Sandbox Code Playgroud) 我采用test_tmp.py了https://docs.python.org/3/library/unittest.mock.html#patch-multiple
from unittest.mock import DEFAULT, MagicMock, patch
thing = object()
other = object()
@patch.multiple('__main__', thing=DEFAULT, other=DEFAULT)
def test_function(thing, other):
print(f'thing={thing}')
print(f'other={other}')
assert isinstance(thing, MagicMock)
assert isinstance(other, MagicMock)
test_function()
Run Code Online (Sandbox Code Playgroud)
它与 python 一起运行
python test_tmp.py
thing=<MagicMock name='thing' id='4355085552'>
other=<MagicMock name='other' id='4355243312'>
Run Code Online (Sandbox Code Playgroud)
但它不适用于 pytest 并出现类似错误
pytest test_tmp.py
============================================================================================================= test session starts =============================================================================================================
platform darwin -- Python 3.8.2, pytest-5.4.3, py-1.9.0, pluggy-0.13.1
rootdir: /private/tmp
collected 0 items / 1 error
=================================================================================================================== ERRORS ====================================================================================================================
________________________________________________________________________________________________________ ERROR collecting test_tmp.py _________________________________________________________________________________________________________
test_tmp.py:13: in <module>
test_function() …Run Code Online (Sandbox Code Playgroud) 下面两个测试有什么区别? (如果有的话)
**在 python 3.10 中
import unittest
from unittest.mock import Mock, patch
class Potato(object):
def spam(self, n):
return self.foo(n=n)
def foo(self, n):
return self.bar(n)
def bar(self, n):
return n + 2
class PotatoTest(unittest.TestCase):
def test_side_effect(self):
spud = Potato()
with patch.object(spud, 'foo', side_effect=spud.foo) as mock_foo:
forty_two = spud.spam(n=40)
mock_foo.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
def test_wraps(self):
spud = Potato()
with patch.object(spud, 'foo', wraps=spud.foo) as mock_foo:
forty_two = spud.spam(n=40)
mock_foo.assert_called_once_with(n=40)
self.assertEqual(forty_two, 42)
Run Code Online (Sandbox Code Playgroud)
一个用于side_effect保留原始方法,而另一个用于wraps有效地做同样的事情(或者至少据我所知)。
python-mock ×10
python ×8
mocking ×4
pytest ×2
python-2.7 ×2
unit-testing ×2
celery ×1
python-3.10 ×1
python-3.x ×1