2

I have a class with a single class attribute that I want to mock

my_class.py

class MyClass:

    __attribute = ['123', '456']

test_my_class.py

import pytest
from directory.my_class import MyClass

def test_1(mocker):
    with mocker.patch.object(MyClass, '__attribute', {'something': 'new'}):
        test = MyClass()

I get:

E           AttributeError: <class 'directory.my_class.MyClass'> does not have the attribute '__attribute'

I also get the same error when trying:

def test_2(mocker):
    with mocker.patch('directory.my_class.MyClass.__attribute', new_callable=mocker.PropertyMock) as a:
        a.return_value = {'something': 'new'}
        test = MyClass()

I've also tried a direct assignment along with the other suggestions in this post: Better way to mock class attribute in python unit test

My project is using a mocker fixture from this plugin: https://pypi.org/project/pytest-mock/

newprogrammer
  • 600
  • 10
  • 22
  • 5
    `__attribute` is subject to name mangling; I would avoid using `__`-prefixed names until you see a reason to use one. If you want to suggest that it is private to the class, `_attribute` will be sufficient. – chepner Sep 23 '21 at 15:36
  • 2
    IMO I would avoid using double underscore class attributes. The name mangling has more headaches than it's worth. Also if a variable is private, then tests should ideally not be accessing it. Make it a defaultable ctor parameter so you can place appropriate values in tests without patching. – flakes Sep 23 '21 at 15:38
  • thanks both - will avoid the double underscore – newprogrammer Sep 23 '21 at 16:11

1 Answers1

5

You can do above with a PropertyMock

from unittest.mock import patch, PropertyMock

class MyClass:
  attr = [1, 2, 3]

with patch.object(MyClass, "attr", new_callable=PropertyMock) as attr_mock:
  attr_mock.return_value = [4, 5, 6]

  print(MyClass.attr) # prints [4, 5, 6]

print(MyClass.attr) # prints [1, 2, 3]

For a docs reference: https://docs.python.org/3/library/unittest.mock.html#unittest.mock.PropertyMock

Eelke van den Bos
  • 1,423
  • 1
  • 13
  • 18