2

I want test that a function has called the __copy__ method (with a given set of arguments) on an object that was passed to it.

Since a DB connection is required to create the object, I thought I should mock it and pass the mock to the function for the test.

However, it seems that __copy__ is returning a mock (not sure if Mock or MagicMock) instance that does not allow arbitrary methods to be called on it. Since my code is calling methods on the new object, it returns the error below.

How can I assert that the __copy__ method was called and still have the rest of the function run without error?

FWIW, I tried assigning old_obj.__class__.return_value = MagicMock() but it seems it's not returning what I assign to it (i.e. I also assigned ... = 'foo' but it still returned a mock object).

Example:

class MyClass:
    def __init__(self, my_name):
        self.my_name = my_name

    def speak(self):
        print('My name is ' + self.my_name)


def my_func(old_obj, new_name):
    new_obj = old_obj.__class__(new_name)
    new_obj.speak()


def test_my_func():
    old_obj = MagicMock()
    old_obj.my_name = 'foo'

    my_func(old_obj, 'bar')

Error produced by py.test:

self = <MagicMock spec='str' id='4490339552'>, name = 'speak'

    def __getattr__(self, name):
        if name in {'_mock_methods', '_mock_unsafe'}:
            raise AttributeError(name)
        elif self._mock_methods is not None:
            if name not in self._mock_methods or name in _all_magics:
>               raise AttributeError("Mock object has no attribute %r" % name)
E               AttributeError: Mock object has no attribute 'speak'
Jacob Beauchamp
  • 532
  • 5
  • 18

1 Answers1

0

You need to provide the spec argument when creating your mock, otherwise any calls or attributes will return a mock.Mock() object.

import unittest
from unittest import mock

class MyClass:
    def __init__(self, my_name):
        self.my_name = my_name
    def speak(self):
        print('My name is ' + self.my_name)

def my_func(old_obj, new_name):
    new_obj = old_obj.__class__(new_name)
    new_obj.speak()

def test_my_func():
    old_obj = mock.MagicMock(spec=MyClass)
    old_obj.my_name = 'foo'
    my_func(old_obj, 'bar')

if __name__ == '__main__':
    unittest.main()
wholevinski
  • 3,658
  • 17
  • 23