Python unittest补丁模拟整个类

时间:2020-10-30 14:22:10

标签: python unit-testing mocking pytest patch

我有一个要在单元测试中打补丁的类。

class OriginalClass():
   def method_a():
     # do something
   
   def method_b():
     # do another thing

现在我创建了另一个类来对其进行修补,因此用于对其进行修补的代码就像

class MockClass(OriginalClass):
    def method_a():
        # This will override the original method and return custom response for testing.

patcher = patch('OriginalClass', new=MockClass)
mock_instance = patcher.start()    

这完全符合我的期望,我可以返回单元测试所需的任何响应。

现在这个问题是当我想验证单元测试中使用正确参数调用的方法时。 我尝试过

mock_instance.method_a.assert_called_once()

但是它失败,并显示错误AttributeError: 'function' object has no attribute 'assert_called_once'

如何在这里测试方法调用?

2 个答案:

答案 0 :(得分:0)

AttributeError:“函数”对象没有属性“ assert_drawn_once”。

创建模拟对象后,就不存在method_a,您必须在断言之前调用一次m.method_a()

    m = mock.create_autospec(OriginalClass)
    m.method_a()
    m.method_a.assert_called_once()

修补整个类的模拟

我把它当作整个类及其所有方法的模拟,我从这里举一个例子 https://docs.python.org/3.3/library/unittest.mock-examples.html

对每个测试方法都应用相同的补丁,这是我的示例,为每个方法和每个测试将整个Primary类作为MockPrimay进行补丁,可以为所需的方法添加setup or SetupClass,即使整个类都被模拟了,但并非测试中要使用的所有方法。

from tests.lib.primary_secondary import Secondary


@mock.patch('tests.lib.primary_secondary.Primary')
class TestSecondaryMockPrimary(unittest.TestCase):

    def test_method_d(self, MockPrimary):
        
        MockPrimary().process()
        MockPrimary().process.return_value = 1
        oc = Secondary()
        self.assertEqual(oc.method_d(), 1)
        import tests
        self.assertIs(tests.lib.primary_secondary.Primary, MockPrimary)

此考试的辅助考试需要主考试

class Primary(object):

    def __init__(self, param):
        self._param = param

    def process(self):
        if self._param == 1:
            self._do_intermediate_process()
        self._do_process()


class Secondary(object):

    def __init__(self):
        self.scl = Primary(1)

    def method_d(self):
        return self.scl.process

答案 1 :(得分:0)

我认为wraps在这里可能会有用:

from unittest.mock import patch

class Person:
    name = "Bob"
    def age(self):
        return 35

class Double(Person):
    def age(self):
        return 5


with patch('__main__.Person', wraps=Double()) as mock:
    print(mock.name)  # mocks data
    print(mock.age()) # runs real methods, but still spies their calls
    mock.age.assert_not_called()

输出:

<MagicMock name='Person.name' id='139815250247536'>
5
...
raise AssertionError(msg)
AssertionError: Expected 'age' to not have been called. Called 1 times.
Calls: [call()].