patching a class yields “AttributeError: Mock object has no attribute” when accessing instance attributes

前端 未结 1 1452
孤城傲影
孤城傲影 2020-12-24 10:44

The Problem
Using mock.patch with autospec=True to patch a class is not preserving attributes of instances of that class.

相关标签:
1条回答
  • 2020-12-24 11:18

    No, autospeccing cannot mock out attributes set in the __init__ method of the original class (or in any other method). It can only mock out static attributes, everything that can be found on the class.

    Otherwise, the mock would have to create an instance of the class you tried to replace with a mock in the first place, which is not a good idea (think classes that create a lot of real resources when instantiated).

    The recursive nature of an auto-specced mock is then limited to those static attributes; if foo is a class attribute, accessing Foo().foo will return an auto-specced mock for that attribute. If you have a class Spam whose eggs attribute is an object of type Ham, then the mock of Spam.eggs will be an auto-specced mock of the Ham class.

    The documentation you read explicitly covers this:

    A more serious problem is that it is common for instance attributes to be created in the __init__ method and not to exist on the class at all. autospec can’t know about any dynamically created attributes and restricts the api to visible attributes.

    You should just set the missing attributes yourself:

    @patch('foo.Foo', autospec=TestFoo)
    def test_patched(self, mock_Foo):
        mock_Foo.return_value.foo = 'foo'
        Bar().bar()
    

    or create a subclass of your Foo class for testing purposes that adds the attribute as a class attribute:

    class TestFoo(foo.Foo):
        foo = 'foo'  # class attribute
    
    @patch('foo.Foo', autospec=TestFoo)
    def test_patched(self, mock_Foo):
        Bar().bar()
    
    0 讨论(0)
提交回复
热议问题