Gary van der Merwe
Gary van der Merwe

Reputation: 9543

unittest.mock.patch: Context manager vs setUp/tearDown in unittest

There seems to be 2 ways to use unittest.mock.patch: is one way better?

Using a context manager and with statement:

class MyTest(TestCase):
    def test_something(self):
        with patch('package.module.Class') as MockClass:
            assert package.module.Class is MockClass

Or calling start and stop from setup and tearDown/cleanup:

class MyTest(TestCase):
    def setUp(self):
        patcher = patch('package.module.Class')
        self.MockClass = patcher.start()
        self.addCleanup(patcher.stop)

    def test_something(self):
        assert package.module.Class is self.MockClass

The context manager version is less code, and so arguable easier to read. I there any reason why I should prefer to use the TestCase setUp/tearDown infrastructure?

Upvotes: 7

Views: 2980

Answers (2)

ely
ely

Reputation: 77484

There is yet a third way to use it, as a decorator:

class MyTest(testcase):

    @unittest.mock.patch('package.module.Class')
    def test_something(self):
        assert package.module.Class is self.MockClass

This is even less code, but that may not be relevant.

There are a few considerations: (1) (as pointed out by babbageclunk) if you will need to reuse the patch, then a plain, boring call to construct one in setUp is best, and is easily readable. (2) if you want to create any metaprogramming facilities so that you can turn on or off the patching when you run tests, then the decorator approach will save you a lot of trouble. In that case, you can write an additional decorator, or use a global variable (ick) to control whether the patch decorators get applied to the test functions or not. If they are embedded inside the function definitions, then you have to manually deal with them if you ever want to turn off patching when running tests. One simple reason why you might want this is merely to run the tests with no patching to induce lots of failures and observe which pieces you have not implemented yet (your decorator for metaprogramming the patches, in fact, could catch these issues and print nice NotImplemented exceptions for you, or even generate a report containing such things). There could be many more reasons to want fine control over whether (and to what degree) patching is "dispatched" in the test suite at a given time.

The decorator approach is also nice in that (a) it lets you isolate which patches go to which test functions in a manner that is outside of that function, but without committing it to setUp, and (b) it makes it very clear to the reader when a given function requires a given patch.

The context manager version does not seem to have many benefits in this case since it is hardly more readable than the decorator version. But, if there really is only a single case, or a very small set of specific cases, where this is used, then the context manager version would be perfectly fine.

Upvotes: 3

babbageclunk
babbageclunk

Reputation: 8751

The main reason to prefer patching in the setUp would be if you had more than one test that needed that class patched. In that case, you'd need to duplicate the with statement in each test.

If you only have one test that needs the patch, I'd prefer the with statement for readability.

Upvotes: 3

Related Questions