Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly use django testcase's addCleanup method?

I have a test subclass of APITestCase where I'm using the class-method setUpTestData to create data for my tests and also some mocks. Basically what I want to do is to run mock.patch.stopall (as shown next), but it's not working.

I have based my implementation on THIS ANSWER and I'm using: Django v2.2.4 and djangorestframework v3.10.2

import mock
from rest_framework.test import APITestCase


class FooTest(APITestCase):

    @classmethod
    def setUpTestData(cls):
        patcher_one = mock.patch('route.one')
        mock_route_one = patcher_one.start()

        patcher_two = mock.patch('route.two')
        mock_route_one = patcher_two.start()

        cls.addCleanup(mock.patch.stopall)

        # etc

        super(FooTest, cls).setUpTestData()

When running my tests with this code, I get:

TypeError: addCleanup() missing 1 required positional argument: 'function'

So I edit the addCleanup call to:

cls.addCleanup(function=mock.patch.stopall)

but I get the following:

TypeError: addCleanup() missing 1 required positional argument: 'self'

Editing to:

cls.addCleanup(cls, function=mock.patch.stopall)

I get

AttributeError: type object 'FooTest' has no attribute '_cleanups'

At this point I'm a bit lost.

The workaround that I'm using is to do it in the tearDownClass method:

@classmethod
def tearDownClass(cls):
    mock.patch.stopall()

But I would like to centralize all the testing logic in the setUpTestData method.

Anyone see's where I'm messing up?

like image 774
nnov Avatar asked Sep 02 '25 09:09

nnov


1 Answers

You cannot call an instance method without an instance. Django's setUpTestData is a classmethod.

code of addCleanUp (Django subclasses Unittest.TestCase):

def addCleanup(self, function, *args, **kwargs):
    """Add a function, with arguments, to be called when the test is
    completed. Functions added are called on a LIFO basis and are
    called after tearDown on test failure or success.

    Cleanup items are called even if setUp fails (unlike tearDown)."""
    self._cleanups.append((function, args, kwargs))

What you should do is move your mocks to the setup method. Firstly you should call self.addcleanup(patch.stopall) to make sure your mocks are stopped even when an error occurs in the setup method (sometimes it is possible), next you start your mock. This is explained in the python doc's here.

The following code should look something similar to this:

class FooTestCase(TestCase):
    def setUp(self):
        super().setUp()
        self.addCleanup(patch.stopall)
        patch('route.one').start()
like image 51
Dawid Dave Kosiński Avatar answered Sep 03 '25 23:09

Dawid Dave Kosiński