Python unittest patch mock entier class

邮差的信 提交于 2021-02-11 13:11:42

问题


I have a class that I want to patch in my unittests.

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

Now I created another class to patch it with, so the code for patching it is like

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()    

This works exactly as I want it to and I can return whatever responses required for my unittests.

Now this issue is when I want to verify that a method is called with the right parameters in the unittests. I tried

mock_instance.method_a.assert_called_once()

But it fail with error AttributeError: 'function' object has no attribute 'assert_called_once'.

How can I test the method calls here?


回答1:


AttributeError: 'function' object has no attribute 'assert_called_once'.

Once mock object is created, there is no method_a exists, you have to call once m.method_a() before assert.

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

patch mock entire class

I took it as mock the whole class and all its methods, I would take an example from here https://docs.python.org/3.3/library/unittest.mock-examples.html

Applying the same patch to every test method, Here is my example, patch the entire Primary class as MockPrimay for every methods and every tests, setup or SetupClass could be added for the methods needed, even the whole class is mocked, but not every methods to be used in the tests.

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)

The Primary is needed for the Secondary for this test

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



回答2:


I think wraps can be useful here:

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()

Output:

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


来源:https://stackoverflow.com/questions/64610170/python-unittest-patch-mock-entier-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!