0

I'm trying to test a python class. I want to mock some of the class's methods but not all of them. This can be achieved well enough with mock.patch. My problem is that:

  1. There are numerous functions to patch so this means many nested calls to patch.
  2. There are many tests and due to the scoped nature of patches this means I need to patch on each test (there are many tests for this class which need the same patch).

Is there any clean solution to this?

To give some more colour, here's an example:

# test_me.py
class ClassUnderTest:

    # All foo functions should be mocked
    def foo1(self):
        pass

    def foo2(self):
        pass

    def foo3(self):
        pass                

    # All bar functions should NOT be mocked
    def bar1(self):
        pass

    def bar2(self):
        pass

    def bar3(self):
        pass                        


# test.py

@mock.patch.object(test_me.ClassUnderTest, 'foo3', autospec=True)
@mock.patch.object(test_me.ClassUnderTest, 'foo2', autospec=True)
@mock.patch.object(test_me.ClassUnderTest, 'foo1', autospec=True)
class MyTestCase(unittest.TestCase):

    def setup_mocks(self, mock_foo1, mock_foo2, mock_foo3):
        # Do things like assign side_effect or return_value to each of the foo mocks
        pass

    def test_1(self, mock_foo1, mock_foo2, mock_foo3):
        self.setup_mocks(mock_foo1, mock_foo2, mock_foo3)
        # Do test_1 tests

    def test_2(self, mock_foo1, mock_foo2, mock_foo3):
        self.setup_mocks(mock_foo1, mock_foo2, mock_foo3)        
        # Do test_2 tests

This solution gets increasingly less clean the more classes and functions I need to mock. Is there a better solution to this?

I'm prepared to accept the answer 'you need to refactor your code to be more testable'. I'm used to designing testing into the architecture from the C/C++ world. However, I was hoping for better from a dynamic language. I've been using python for a long time but never had to delve to deep into mocking.

Andrew Parker
  • 1,425
  • 2
  • 20
  • 28
  • Also, an addendum to this, which is more of a comment really. It's annoying that mocking can't be done in test `setUp` due to scoped nature of mocks. – Andrew Parker Jan 11 '17 at 10:54

1 Answers1

0

One thing that might help you is stubbing out the class undertest in the setup method.

class ClassUnderTestStub(ClassUnderTest):
  def __init__(self, *args, **kwargs):
    self.arbitrary_attr = None
    ...

def setUp(self):
  self.helper = self.ClassUnderTestStub()
  self.helper.arbitrary_function = mock.MagicMock(...)

def testFoo(self):
  self.helper.foo_dependency = mock.MagicMock(...)
Dan
  • 1,874
  • 1
  • 16
  • 21