0

I have a question similar to the questions posted here, which also have no satisfying answer yet:
mocking a method with another reusable method with arguments in python
Using mock patch to mock an instance method

I have the following Production class and UnitTest class. In the second test case in the UnitTest class, I want to substitute the real method for the fake method, which I have defined above the Unit Test class. However, I am unable to connect the method even_numbers in the system under test to the fake method odd_numbers, including its input arguments. How should this substition look like? I am using Python 2.7.

import unittest
from mock import patch

class ProdClass(object):        
    def even_numbers(self, numbers):
        print 'This is the real method'
        return [n for n in numbers if n%2 == 0]

def odd_numbers(numbers):
    print 'This is the mock'
    return [n for n in numbers if n%2 != 0]

class MockingTestTestCase(unittest.TestCase):
    def test_even_numbers_01(self):
        sut = ProdClass()        

        result = sut.even_numbers(range(10))
        expected_result = [0, 2, 4, 6, 8]
        self.assertEqual(result, expected_result,  
                         msg = '\nRetrieved: \n{0}\nExpected: \n{1}'.format(result, expected_result))

    @patch('ProdClass.even_numbers') 
    def test_even_numbers_02(self, mock_even_numbers):
        sut = ProdClass()
        '''Substitute real method even_numbers with mocked method even_numbers here'''
        mock_even_numbers.return_value = odd_numbers

        result = sut.even_numbers(range(10))
        expected_result = [1, 3, 5, 7, 9]
        self.assertEqual(result, expected_result, 
                         msg = '\nRetrieved: \n{0}\nExpected: \n{1}'.format(result, expected_result))

if __name__ == "__main__":
    unittest.main()

EDIT: With the help of Martijn below, I have edited the code into this working example:

import unittest
from mock import patch

class ProdClass(object):

    def even_numbers(self, numbers):
        print 'This is the real method'
        return [n for n in numbers if n%2 == 0]

def odd_numbers(numbers):
    print 'This is the mock'
    return [n for n in numbers if n%2 != 0]

class MockingTestTestCase(unittest.TestCase):
    def test_even_numbers_01(self):
        sut = ProdClass()        

        result = sut.even_numbers(range(10))
        expected_result = [0, 2, 4, 6, 8]
        self.assertEqual(result, expected_result,  
                         msg = '\nRetrieved: \n{0}\nExpected: \n{1}'.format(result, expected_result))

    @patch.object(ProdClass, 'even_numbers') 
    def test_even_numbers_02(self, mock_even_numbers):
        sut = ProdClass()
        '''Substitute real method even_numbers with mocked method even_numbers here'''
        mock_even_numbers.side_effect = odd_numbers

        result = sut.even_numbers(range(10))
        expected_result = [1, 3, 5, 7, 9]
        self.assertEqual(result, expected_result, 
                         msg = '\nRetrieved: \n{0}\nExpected: \n{1}'.format(result, expected_result))

if __name__ == "__main__":
    unittest.main()
Community
  • 1
  • 1
physicalattraction
  • 6,485
  • 10
  • 63
  • 122
  • 1
    You did not read the second post you linked to properly. They use `patch.object()` there, not `patch()`. `@patch.object(ProdClass, 'even_numbers')` would do what you wanted. – Martijn Pieters Sep 05 '14 at 08:43
  • I still don't understand how to use it. If I add `@patch.object(ProdClass, 'even_numbers')` to the top of the test method, and keep the rest the same, the return value (stored in `result`) is an instance of the function `odd_numbers`, not the return value of that function. How should I *call* that function `odd_numbers` with the proper argument? – physicalattraction Sep 05 '14 at 08:51
  • 1
    `return_value` is what calling `even_numbers` returns when called, yes. It sounds like you wanted to use `side_effect` instead. – Martijn Pieters Sep 05 '14 at 08:57
  • That indeed does exactly what I want it to do, thanks! I find the name `side_effects` misleading though. Is there a reason it is called side effects, while you're actually changing the entire implementation? – physicalattraction Sep 05 '14 at 09:01
  • Mocking is not usually about changing the implementation; you usually just produce a *static* return value. `side_effect` is for the complex situations, like raising an exception, or handling multiple calls where the static value returned depends on more complex determinations from the arguments. – Martijn Pieters Sep 05 '14 at 09:02

0 Answers0