3

I have a system under test (Class Printer below), which uses another class (Class ContentContainer below). In one method (Method retrieve_and_show_content below), this class is instantiated. In the test for this method (Method test_printer_03 below), I want to instantiate a mock instead of the real class. However, it doesn't work like this.

I read here that I should change the object that a name points to with another one. It seems like the name of the object that I want to replace is simply ContentContainer, while the name of the object that I am actually replacing is TestMockClass.ContentContainer. Is this observation correct? If so, how do I change this? If I simply remove the prefix TestMockClass in the patch statement, I get a TypeError: Need a valid target to patch. You supplied: 'ContentContainer'.

#TestMockClass.py import unittest from mock import Mock, patch class Printer(): def __init__(self, name, cc): self.name = name self.cc = cc def show_content(self): text = '{0} says: {1}'.format(self.name, self.cc.content()) return text def retrieve_and_show_content(self): cc_tmp = ContentContainer() text = '{0} says: {1}'.format(self.name, cc_tmp.content()) return text class ContentContainer(): def __init__(self): self.method_counter() def content(self): return 'Content from ContentContainer' def method_counter(self): pass class Test(unittest.TestCase): '''No mocking''' def test_printer_01(self): cc = ContentContainer() sut = Printer('P01', cc) result = sut.show_content() expected_result = 'P01 says: Content from ContentContainer' self.assertEqual(result, expected_result, msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) result = sut.retrieve_and_show_content() expected_result = 'P01 says: Content from ContentContainer' self.assertEqual(result, expected_result, msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) '''Create a mock object, which is the input of the method under test''' def test_printer_02(self): mock_cc = Mock() mock_cc.content.return_value = 'Mocked content' sut = Printer('P02', mock_cc) result = sut.show_content() expected_result = 'P02 says: Mocked content' self.assertEqual(result, expected_result, msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) self.assertFalse(mock_cc.method_counter.called, 'Method method_counter shall not be called') '''Create a mock class, which is instantiated inside the method under test''' @patch('TestMockClass.ContentContainer') def test_printer_03(self, mock_cc): mock_cc.content.return_value = 'Mocked content' sut = Printer('P03', mock_cc) result = sut.retrieve_and_show_content() expected_result = 'P03 says: Mocked content' self.assertEqual(result, expected_result, msg = '\nRetrieved:\n{0} \nExpected:\n{1}'.format(result, expected_result)) self.assertFalse(mock_cc.method_counter.called, 'Method method_counter shall not be called') if __name__ == "__main__": unittest.main() 

When this unittest is run, the output is:

AssertionError: Retrieved: P03 says: Content from ContentContainer Expected: P03 says: Mocked content 

1 Answer 1

14

Two things:

  1. Because ContentContainer is now in the same file as the tests, you actually need to patch __main__.ContentContainer:

    @patch('__main__.ContentContainer') 
  2. Because ContentContainer is a class and you're calling content on an instance of that class, you actually wish to mock content on that instance, not on the class. You therefore need to do: mock_cc.return_value.content.return_value = 'Mocked content' (note the additional .return_value in there to make sure you're mocking the instance, not the class). This is because calling a class creates an instance. So, the instance is the return value of the call on the class.

The test should thus look like:

@patch('__main__.ContentContainer') def test_printer_03(self, mock_cc): mock_cc.return_value.content.return_value = 'Mocked content' ... 
Sign up to request clarification or add additional context in comments.

1 Comment

Cool, from the docs - This means you access the “mock instance” by looking at the return value of the mocked class.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.