问题描述
我想测试我编写的电子邮件发送方法.在文件format_email.py中,我导入了send_email.
I would like to test a email sending method I wrote. In file, format_email.py I import send_email.
from cars.lib.email import send_email
class CarEmails(object):
def __init__(self, email_client, config):
self.email_client = email_client
self.config = config
def send_cars_email(self, recipients, input_payload):
在send_cars_email()中格式化电子邮件内容之后,我使用之前导入的方法发送电子邮件.
After formatting the email content in send_cars_email() I send the email using the method I imported earlier.
response_code = send_email(data, self.email_client)
在我的测试文件test_car_emails.py中
in my test file test_car_emails.py
@pytest.mark.parametrize("test_input,expected_output", test_data)
def test_email_payload_formatting(test_input, expected_output):
emails = CarsEmails(email_client=MagicMock(), config=config())
emails.send_email = MagicMock()
emails.send_cars_email(*test_input)
emails.send_email.assert_called_with(*expected_output)
当我运行测试时,它在未调用断言时失败.我相信问题出在我嘲笑send_email函数的地方.
When I run the test it fails on assertion not called. I believe The issue is where I am mocking the send_email function.
我应该在哪里嘲笑此功能?
Where should I be mocking this function?
推荐答案
用emails.send_email = MagicMock()
行嘲笑的是函数
class CarsEmails:
def send_email(self):
...
您没有的.因此,此行只会为emails
对象 add 添加新功能.但是,该函数不会从您的代码中调用,并且赋值将完全无效.相反,您应该从cars.lib.email
模块中模拟函数send_email
.
that you don't have. This line will thus only add a new function to your emails
object. However, this function is not called from your code and the assignment will have no effect at all. Instead, you should mock the function send_email
from the cars.lib.email
module.
一旦通过模块format_email.py
中的from cars.lib.email import send_email
导入了功能send_email
,该功能将以名称format_email.send_email
可用.由于您知道该函数已在此处调用,因此可以使用其新名称对其进行模拟:
Once you have imported the function send_email
via from cars.lib.email import send_email
in your module format_email.py
, it becomes available under the name format_email.send_email
. Since you know the function is called there, you can mock it under its new name:
from unittest.mock import patch
from format_email import CarsEmails
@pytest.mark.parametrize("test_input,expected_output", test_data)
def test_email_payload_formatting(config, test_input, expected_output):
emails = CarsEmails(email_client=MagicMock(), config=config)
with patch('format_email.send_email') as mocked_send:
emails.send_cars_email(*test_input)
mocked_send.assert_called_with(*expected_output)
模拟定义的功能
更新:
阅读在何处unittest
文档中的补丁程序(另请参见评论来自 Martijn Pieters 建议):
It really helps to read the section Where to patch in the unittest
docs (also see the comment from Martijn Pieters suggesting it):
因此,请在使用位置坚持使用该函数的模拟方法,而不要先刷新导入或以正确的顺序对齐它们.即使format_email
的源代码由于某种原因而无法访问(例如,它是经过Cythonized/编译的C/C ++扩展模块)时,即使有一些晦涩的用例,您仍然只有两种可能的导入方式,因此,只需尝试在何处描述的两种模拟方法补丁并使用成功的补丁.
So stick with the mocking of the function in usage places and don't start with refreshing the imports or aligning them in correct order. Even when there should be some obscure usecase when the source code of format_email
would be inaccessible for some reason (like when it is a cythonized/compiled C/C++ extension module), you still have only two possible ways of doing the import, so just try out both mocking possibilities as described in Where to patch and use the one that succeeds.
原始答案:
您还可以在其原始模块中模拟send_email
函数:
You can also mock send_email
function in its original module:
with patch('cars.lib.email.send_email') as mocked_send:
...
,但是请注意,如果您在修补之前在format_email.py
中调用了send_email
的导入,则由于已经导入了该功能,因此修补cars.lib.email
对format_email
中的代码不会产生任何影响.在下面的示例中,mocked_send
不会被调用:
but be aware that if you have called the import of send_email
in format_email.py
before the patching, patching cars.lib.email
won't have any effect on code in format_email
since the function is already imported, so the mocked_send
in the example below won't be called:
from format_email import CarsEmails
...
emails = CarsEmails(email_client=MagicMock(), config=config)
with patch('cars.lib.email.send_email') as mocked_send:
emails.send_cars_email(*test_input)
mocked_send.assert_called_with(*expected_output)
要解决此问题,您应该在cars.lib.email
的修补程序之后首次导入format_email
:
To fix that, you should either import format_email
for the first time after the patch of cars.lib.email
:
with patch('cars.lib.email.send_email') as mocked_send:
from format_email import CarsEmails
emails = CarsEmails(email_client=MagicMock(), config=config)
emails.send_cars_email(*test_input)
mocked_send.assert_called_with(*expected_output)
或重新加载模块,例如与importlib.reload()
:
or reload the module e.g. with importlib.reload()
:
import importlib
import format_email
with patch('cars.lib.email.send_email') as mocked_send:
importlib.reload(format_email)
emails = format_email.CarsEmails(email_client=MagicMock(), config=config)
emails.send_cars_email(*test_input)
mocked_send.assert_called_with(*expected_output)
如果您问我,这两种方法都不是很好.我会坚持在称为它的模块中模拟该函数.
Not that pretty either way, if you ask me. I'd stick with mocking the function in the module where it is called.
这篇关于用pytest模拟导入的功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!