你用这条线嘲笑的emails.send_email = MagicMock()
是功能
class CarsEmails:
def send_email(self):
...
你没有的。因此,此行只会向您的对象添加一个新功能。emails
但是,不会从您的代码中调用此函数,并且分配将完全没有效果。相反,您应该模拟模块中的send_email
函数cars.lib.email
。
模拟使用它的函数
一旦你在你的模块send_email
中导入了这个函数,它就可以在 name 下使用。由于您知道该函数在那里被调用,您可以使用它的新名称来模拟它:from cars.lib.email import send_email
format_email.py
format_email.send_email
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)
模拟定义它的函数
更新:
阅读文档中的修补位置部分确实很有帮助(另请参阅Martijn Pieters的评论):unittest
基本原则是您在查找对象的位置进行修补,该位置不一定与定义对象的位置相同。
所以坚持在使用位置模拟函数,不要从刷新导入或以正确的顺序对齐它们开始。即使当源代码format_email
由于某种原因而无法访问时(例如当它是 cythonized/编译的 C/C++ 扩展模块时)应该有一些晦涩的用例,您仍然只有两种可能的导入方式,所以请尝试如在哪里修补和使用成功的那个中所述的两种模拟可能性。
原始答案:
您还可以send_email
在其原始模块中模拟函数:
with patch('cars.lib.email.send_email') as mocked_send:
...
但请注意,如果您在修补之前调用了send_email
in的导入format_email.py
,则修补cars.lib.email
不会对代码中的代码产生任何影响,format_email
因为该函数已经导入,因此mocked_send
不会调用以下示例中的:
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)
要解决此问题,您应该format_email
在以下补丁后首次导入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()
:
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)
如果你问我,这两种方式都不漂亮。我会坚持在调用它的模块中模拟该函数。