How to mock Python static methods and class methods

前端 未结 3 2030
不知归路
不知归路 2021-02-07 17:18

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

class Calculator(objec         


        
3条回答
  •  执笔经年
    2021-02-07 18:10

    C#, Java and C++ programmers tend to overuse class and static methods in Python. The Pythonic approach is to use module functions.

    So first, here is the refactored software under test, with methods increment() and decrement() as module functions. The interface does change, but the functionality is the same:

    # Module machines
    
    class Calculator(object):
        def __init__(self, multiplier):
            self._multiplier = multiplier
        def multiply(self, n):
            return self._multiplier * n
    
    def increment(n):
        return n + 1
    
    def decrement(n):
        return n - 1
    
    calculator = Calculator(2)
    assert calculator.multiply(3) == 6
    assert increment(3) == 4
    assert decrement(3) == 2
    
    
    class Machine(object):
        '''A larger machine that has a calculator.'''
        def __init__(self, calculator):
            self._calculator = calculator
        def mult(self, n):
            return self._calculator.multiply(n)
        def incr(self, n):
            return increment(n)
        def decr(self, n):
            return decrement(n)
    
    machine = Machine(Calculator(3))
    assert machine.mult(3) == 9
    assert machine.incr(3) == 4
    assert machine.decr(3) == 2
    

    Add functions increment_mock() and decrement_mock() to mock increment() and decrement():

    from mock import Mock
    import machines
    
    def MockCalculator(multiplier):
        mock = Mock(spec=machines.Calculator, name='MockCalculator')
    
        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_mock(n):
        '''Increment by 2 instead of 1 so we can see the difference.'''
        return n + 2
    
    def decrement_mock(n):
        '''Decrement by 2 instead of 1 so we can see the difference.'''
        return n - 2
    

    And now for the good part. Patch increment() and decrement() to replace them with their mocks:

    import unittest
    from mock import patch
    import machines
    
    @patch('machines.increment', increment_mock)
    @patch('machines.decrement', decrement_mock)
    class TestMachine(unittest.TestCase):
        def test_mult(self):
            '''The bound method of Calculator is replaced with MockCalculator'''
            machine = machines.Machine(MockCalculator(3))
            self.assertEqual(machine.mult(3), 18)
    
        def test_incr(self):
            '''increment() is replaced with increment_mock()'''
            machine = machines.Machine(MockCalculator(3))
            self.assertEqual(machine.incr(3), 5)
    
        def test_decr(self):
            '''decrement() is replaced with decrement_mock()'''
            machine = machines.Machine(MockCalculator(3))
            self.assertEqual(machine.decr(3), 1)
    

提交回复
热议问题