如何在 Python 单元测试中模拟 class?
How do I mock a class in a Python unit test?
我有一个 class:
class A:
__init__(self):
self.b = B()
def is_authorized(self)
name = self.b.get_name()
if name == 'admin':
return True
else:
return False
我想编写一个单元测试来测试 is_authorized
方法。问题是它需要一个 B
class 的实例,因为它需要网络连接和其他东西,所以孤立地构造起来非常复杂。我如何模拟此 class 并提供仅具有 get_name
方法的内容。这样我就可以创建 A
class 并测试该方法。
为 B 创建一个模拟 class,例如:
class MockB(object):
def get_name(self):
return 'admin'
然后,在您的测试用例中,将 A 包装在 class 中,该 class 使用 MockB 而不是常规 B:
class TestA(A):
def __init__(self):
self.b = MockB()
不过,老实说,我不确定这能证明什么。
通过使用 mock
库,您可以修补 B
class 并将其替换为 MagicMock()
对象。 mock
库正是为完成这些工作而设计的,并打破了有问题的对象或真实资源的硬依赖。
在您的简单示例中,完整的测试将是:
module_a.py
class B():
def __init__(self):
print("The awful B class!!!")
def get_name(self):
print("The awful B.get_name() method!!!")
class A():
def __init__(self):
self.b = B()
def is_authorized(self):
name = self.b.get_name()
if name == 'admin':
return True
else:
return False
module_a_test.py
import unittest
from unittest.mock import patch
from module_a import A
class MyTestCase(unittest.TestCase):
# patch B class in a_module by a MagicMock instance
# mock_b_constructor passed to test method
@patch("module_a.B")
def test_a(self, mock_b_constructor):
# B() return value will be the B() instance assigned to a.b property
mock_b = mock_b_constructor.return_value
# Now start test:
a = A()
# Ok! b is our mock...
self.assertIs(a.b, mock_b)
# Not authorized
self.assertFalse(a.is_authorized())
mock_b.get_name.return_value = 'admin'
# Yeah!!! we are admin
self.assertTrue(a.is_authorized())
# Sanity check
mock_b.get_name.return_value = 'guest'
self.assertFalse(a.is_authorized())
Patch 将仅在您的测试方法上下文中存在。这是一个简单直接的示例,说明如何使用 mock
中的模拟和补丁,但实际情况可能会稍微复杂一些。
我有一个 class:
class A:
__init__(self):
self.b = B()
def is_authorized(self)
name = self.b.get_name()
if name == 'admin':
return True
else:
return False
我想编写一个单元测试来测试 is_authorized
方法。问题是它需要一个 B
class 的实例,因为它需要网络连接和其他东西,所以孤立地构造起来非常复杂。我如何模拟此 class 并提供仅具有 get_name
方法的内容。这样我就可以创建 A
class 并测试该方法。
为 B 创建一个模拟 class,例如:
class MockB(object):
def get_name(self):
return 'admin'
然后,在您的测试用例中,将 A 包装在 class 中,该 class 使用 MockB 而不是常规 B:
class TestA(A):
def __init__(self):
self.b = MockB()
不过,老实说,我不确定这能证明什么。
通过使用 mock
库,您可以修补 B
class 并将其替换为 MagicMock()
对象。 mock
库正是为完成这些工作而设计的,并打破了有问题的对象或真实资源的硬依赖。
在您的简单示例中,完整的测试将是:
module_a.py
class B():
def __init__(self):
print("The awful B class!!!")
def get_name(self):
print("The awful B.get_name() method!!!")
class A():
def __init__(self):
self.b = B()
def is_authorized(self):
name = self.b.get_name()
if name == 'admin':
return True
else:
return False
module_a_test.py
import unittest
from unittest.mock import patch
from module_a import A
class MyTestCase(unittest.TestCase):
# patch B class in a_module by a MagicMock instance
# mock_b_constructor passed to test method
@patch("module_a.B")
def test_a(self, mock_b_constructor):
# B() return value will be the B() instance assigned to a.b property
mock_b = mock_b_constructor.return_value
# Now start test:
a = A()
# Ok! b is our mock...
self.assertIs(a.b, mock_b)
# Not authorized
self.assertFalse(a.is_authorized())
mock_b.get_name.return_value = 'admin'
# Yeah!!! we are admin
self.assertTrue(a.is_authorized())
# Sanity check
mock_b.get_name.return_value = 'guest'
self.assertFalse(a.is_authorized())
Patch 将仅在您的测试方法上下文中存在。这是一个简单直接的示例,说明如何使用 mock
中的模拟和补丁,但实际情况可能会稍微复杂一些。