使用带和不带 side_effect 的 @patch() 进行模拟有什么区别?
What is the difference between mocking with @patch() with and without side_effect?
我有一个 script.py
文件:
# in script.py
def my_basic_function(value, c):
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words():
return self.val
和一个 test.py
文件:
# in test.py
from mock import patch
from script import my_basic_function, BasicClass
def third_function(self,):
return "not {}".format(self.val)
def fourth_function():
return "not ponies"
def init_mock(self, val):
self.val = val
@patch('script.BasicClass.words', third_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function():
assert my_basic_function("ponies", BasicClass) == "not ponies"
我可以 运行 使用 pytest test.py
从命令行成功。
如果我想在我的 @patch
中使用 side_effect
,我必须做一些不同的事情:
@patch('script.BasicClass.words', side_effect = fourth_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function(amock):
assert my_basic_function("ponies", BasicClass) == "not ponies"
也就是说,我必须:
- 向
test_my_basic_function
添加一个我从未使用过的参数。
- 调用
fourth_function
而不是 third_function
,因为我不能使用任何 class 实例变量。
这两种方式打补丁有什么区别?
您的操作不需要补丁(示例 A)。您正在传递参数 c,您知道它是 BasicClass 的 class,因此不需要补丁。
如果您在正在测试的函数中引入了您不想实际发生的函数调用或对象初始化,那么您将需要一个补丁。
当使用 @patch
时,我们应该使用 kwarg side_effect
来模拟引发异常,而不是调用其他函数。如果我们想模拟函数的 return 值,那么使用 kwarg return_value
。如果我们只想模拟一个函数,那么我们只使用 @patch
没有任何 kwargs。当我们使用补丁作为装饰器时,我们需要将它们传递给函数。它们确实可以不使用,但我们应该将它们与模拟函数一起使用,例如 assert_called_once
或 assert_called_once_with
以确保您的补丁按预期工作。请参阅示例 B
==============例子A==================
import unittest
def my_basic_function(value, c):
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words(self):
return self.val
class TestMyBasicFunction(unittest.TestCase):
def test_my_basic_class(self):
value = my_basic_function("jeremy", BasicClass)
self.assertEqual("jeremy", value)
def test_my_basic_class_wrong(self):
value = my_basic_function("jeremy", BasicClass)
self.assertNotEqual("w33b", value)
============例子B======================
import unittest
from unittest.mock import patch
def ensure_str(value):
try:
return str(value)
except Exception:
raise TypeError
def my_basic_function(value, c):
value = ensure_str(value)
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words(self):
return self.val
class TestMyBasicFunction(unittest.TestCase):
@patch('script.ensure_str', return_value="jeremy")
def test_return_value(self, ensure_str_mock):
value = my_basic_function("jeremy", BasicClass)
ensure_str_mock.assert_called_once_with("jeremy")
self.assertEqual("jeremy", value)
@patch('script.ensure_str')
def test_no_return_value(self, ensure_str_mock):
value = my_basic_function("jeremy", BasicClass)
self.assertEqual(ensure_str_mock(), value)
@patch('script.ensure_str', side_effect=TypeError)
def test_side_effect(self, ensure_str_mock):
with self.assertRaises(TypeError):
value = my_basic_function({'apple': 'sauce'}, BasicClass)
ensure_str_mock.assert_called_once_with({'apple': 'sauce'})
我有一个 script.py
文件:
# in script.py
def my_basic_function(value, c):
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words():
return self.val
和一个 test.py
文件:
# in test.py
from mock import patch
from script import my_basic_function, BasicClass
def third_function(self,):
return "not {}".format(self.val)
def fourth_function():
return "not ponies"
def init_mock(self, val):
self.val = val
@patch('script.BasicClass.words', third_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function():
assert my_basic_function("ponies", BasicClass) == "not ponies"
我可以 运行 使用 pytest test.py
从命令行成功。
如果我想在我的 @patch
中使用 side_effect
,我必须做一些不同的事情:
@patch('script.BasicClass.words', side_effect = fourth_function)
@patch('script.BasicClass.__init__', init_mock)
def test_my_basic_function(amock):
assert my_basic_function("ponies", BasicClass) == "not ponies"
也就是说,我必须:
- 向
test_my_basic_function
添加一个我从未使用过的参数。 - 调用
fourth_function
而不是third_function
,因为我不能使用任何 class 实例变量。
这两种方式打补丁有什么区别?
您的操作不需要补丁(示例 A)。您正在传递参数 c,您知道它是 BasicClass 的 class,因此不需要补丁。 如果您在正在测试的函数中引入了您不想实际发生的函数调用或对象初始化,那么您将需要一个补丁。
当使用 @patch
时,我们应该使用 kwarg side_effect
来模拟引发异常,而不是调用其他函数。如果我们想模拟函数的 return 值,那么使用 kwarg return_value
。如果我们只想模拟一个函数,那么我们只使用 @patch
没有任何 kwargs。当我们使用补丁作为装饰器时,我们需要将它们传递给函数。它们确实可以不使用,但我们应该将它们与模拟函数一起使用,例如 assert_called_once
或 assert_called_once_with
以确保您的补丁按预期工作。请参阅示例 B
==============例子A==================
import unittest
def my_basic_function(value, c):
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words(self):
return self.val
class TestMyBasicFunction(unittest.TestCase):
def test_my_basic_class(self):
value = my_basic_function("jeremy", BasicClass)
self.assertEqual("jeremy", value)
def test_my_basic_class_wrong(self):
value = my_basic_function("jeremy", BasicClass)
self.assertNotEqual("w33b", value)
============例子B======================
import unittest
from unittest.mock import patch
def ensure_str(value):
try:
return str(value)
except Exception:
raise TypeError
def my_basic_function(value, c):
value = ensure_str(value)
return c(value).words()
class BasicClass:
def __init__(self, val):
self.val = val
def words(self):
return self.val
class TestMyBasicFunction(unittest.TestCase):
@patch('script.ensure_str', return_value="jeremy")
def test_return_value(self, ensure_str_mock):
value = my_basic_function("jeremy", BasicClass)
ensure_str_mock.assert_called_once_with("jeremy")
self.assertEqual("jeremy", value)
@patch('script.ensure_str')
def test_no_return_value(self, ensure_str_mock):
value = my_basic_function("jeremy", BasicClass)
self.assertEqual(ensure_str_mock(), value)
@patch('script.ensure_str', side_effect=TypeError)
def test_side_effect(self, ensure_str_mock):
with self.assertRaises(TypeError):
value = my_basic_function({'apple': 'sauce'}, BasicClass)
ensure_str_mock.assert_called_once_with({'apple': 'sauce'})