How to mock Python static methods and class methods
Question: How do I mock a class that has unbound methods? For example, this class has a
`@classmethod` and a `@staticmethod`:

    
    
    class Calculator(object):
        def __init__(self, multiplier):
            self._multiplier = multiplier
        def multiply(self, n):
            return self._multiplier * n
        @classmethod
        def increment(cls, n):
            return n + 1
        @staticmethod
        def decrement(n):
            return n - 1
    
    calculator = Calculator(2)
    assert calculator.multiply(3) == 6    
    assert calculator.increment(3) == 4
    assert calculator.decrement(3) == 2
    assert Calculator.increment(3) == 4
    assert Calculator.decrement(3) == 2
    

The above pretty much describes my question. The following is a working
example that demonstrates the things I have tried.

Class `Machine` contains an instance of `Calculator`. I will be testing
`Machine` with a mock of `Calculator`. To demonstrate my issue, `Machine`
calls the unbound methods via an instance of `Calculator` and via the
`Calculator` class:

    
    
    class Machine(object):
        def __init__(self, calculator):
            self._calculator = calculator
        def mult(self, n):
            return self._calculator.multiply(n)
        def incr_bound(self, n):
            return self._calculator.increment(n)
        def decr_bound(self, n):
            return self._calculator.decrement(n)
        def incr_unbound(self, n):
            return Calculator.increment(n)
        def decr_unbound(self, n):
            return Calculator.decrement(n)
    
    machine = Machine(Calculator(3))
    assert machine.mult(3) == 9
    
    assert machine.incr_bound(3) == 4
    assert machine.incr_unbound(3) == 4
    
    assert machine.decr_bound(3) == 2
    assert machine.decr_unbound(3) == 2
    

All the functional code above works fine. Next is the part that does not work.

I create a mock of `Calculator` to use in testing `Machine`:

    
    
    from mock import Mock
    
    def MockCalculator(multiplier):
        mock = Mock(spec=Calculator, name='MockCalculator')
    
        def multiply_proxy(n):
            '''Multiply by 2*multiplier instead so we can see the difference'''
            return 2 * multiplier * n
        mock.multiply = multiply_proxy
    
        def increment_proxy(n):
            '''Increment by 2 instead of 1 so we can see the difference'''
            return n + 2
        mock.increment = increment_proxy
    
        def decrement_proxy(n):
            '''Decrement by 2 instead of 1 so we can see the difference'''
            return n - 2
        mock.decrement = decrement_proxy
    
        return mock
    

In the unit test below, the bound methods use `MockCalculator` as I had hoped.
However, the calls to `Calculator.increment()` and `Calculator.decrement()`
still use `Calculator`:

    
    
    import unittest
    
    class TestMachine(unittest.TestCase):
        def test_bound(self):
            '''The bound methods of Calculator are replaced with MockCalculator'''
            machine = Machine(MockCalculator(3))
            self.assertEqual(machine.mult(3), 18)
            self.assertEqual(machine.incr_bound(3), 5)
            self.assertEqual(machine.decr_bound(3), 1)
    
        def test_unbound(self):
            '''Machine.incr_unbound() and Machine.decr_unbound() are still using
            Calculator.increment() and Calculator.decrement(n), which is wrong.
            '''
            machine = Machine(MockCalculator(3))
            self.assertEqual(machine.incr_unbound(3), 4)    # I wish this was 5
            self.assertEqual(machine.decr_unbound(3), 2)    # I wish this was 1
    

So I try to patch `Calculator.increment()` and `Calculator.decrement()`:

    
    
    def MockCalculatorImproved(multiplier):
        mock = Mock(spec=Calculator, name='MockCalculatorImproved')
    
        def multiply_proxy(n):
            '''Multiply by 2*multiplier instead of multiplier so we can see the difference'''
            return 2 * multiplier * n
        mock.multiply = multiply_proxy
        return mock
    
    def increment_proxy(n):
        '''Increment by 2 instead of 1 so we can see the difference'''
        return n + 2
    
    def decrement_proxy(n):
        '''Decrement by 2 instead of 1 so we can see the difference'''
        return n - 2
    
    
    from mock import patch
    
    @patch.object(Calculator, 'increment', increment_proxy)
    @patch.object(Calculator, 'decrement', decrement_proxy)
    class TestMachineImproved(unittest.TestCase):
        def test_bound(self):
            '''The bound methods of Calculator are replaced with MockCalculator'''
            machine = Machine(MockCalculatorImproved(3))
            self.assertEqual(machine.mult(3), 18)
            self.assertEqual(machine.incr_bound(3), 5)
            self.assertEqual(machine.decr_bound(3), 1)
    
        def test_unbound(self):
            '''machine.incr_unbound() and Machine.decr_unbound() should use
            increment_proxy() and decrement_proxy(n).
            '''
            machine = Machine(MockCalculatorImproved(3))
            self.assertEqual(machine.incr_unbound(3), 5)
            self.assertEqual(machine.decr_unbound(3), 1)
    

Even after patching, the unbound methods want an instance of `Calculator` as
an argument:

> TypeError: unbound method increment_proxy() must be called with Calculator
> instance as first argument (got int instance instead)

How do I mock out class method `Calculator.increment()` and static method
`Calculator.decrement()`?


Answer: You were patching the wrong object. You must patch the `Calculator` from the
`Machine` class, not the general `Calculator` class. Read about it
[here](http://alexmarandon.com/articles/python_mock_gotchas/).

    
    
    from mock import patch
    import unittest
    
    from calculator import Calculator
    from machine import Machine
    
    
    class TestMachine(unittest.TestCase):
        def my_mocked_mult(self, multiplier):
            return 2 * multiplier * 3
        def test_bound(self):
            '''The bound methods of Calculator are replaced with MockCalculator'''
            machine = Machine(Calculator(3))
            with patch.object(machine, "mult") as mocked_mult:
                mocked_mult.side_effect = self.my_mocked_mult
                self.assertEqual(machine.mult(3), 18)
                self.assertEqual(machine.incr_bound(3), 5)
                self.assertEqual(machine.decr_bound(3), 1)
    
        def test_unbound(self):
            '''Machine.incr_unbound() and Machine.decr_unbound() are still using
            Calculator.increment() and Calculator.decrement(n), which is wrong.
            '''
            machine = Machine(Calculator(3))
            self.assertEqual(machine.incr_unbound(3), 4)    # I wish this was 5
            self.assertEqual(machine.decr_unbound(3), 2)    # I wish this was 1
    

