mathew
mathew

Reputation: 987

Django unit tests failing when run with other test cases

I'm getting inconsistent behavior with Django unit tests. On my development machine using sqlite, if I run tests on my two apps separately the tests pass, but if I run manage.py test to test everything at once, I start getting unit test failures consistently on two tests.

On my staging server which uses Postgres, I have a particular test that works when testing it individually (e.g. manage.py test MyApp.tests.MyTestCase.testSomething), but fails when running the entire test case (e.g. manage.py test MyApp.tests.TestCase).

Other related StackOverflow questions seem to have two solutions:

  1. Use Django TestCase's instead of the Python equivalent
  2. Use TransactionTestCase's to make sure the database is cleaned up properly after every test.

I've tried both to no avail. Out of frustration, I also tried using django-nose instead, but I was seeing the same errors. I'm on Django 1.6.

Upvotes: 7

Views: 3557

Answers (3)

Beehive
Beehive

Reputation: 23

I had the same thing happening today with a series of tests. I had 23 regular django.test.TestCase tests and then one django.contrib.staticfiles.testing.StaticLiveServerTestCase test. It was that final test that would always fail when ran with the rest of them but pass on its own.

Solution

In the 23 regular TestCase tests I really had implemented a subclass of the regular TestCase so that I could provide some common functionality specific to my application to the tests. In the tearDown methods I had failed to call the super method. Once I called the super method in the tearDown method, it worked. So the lesson here was to check to make sure you are cleaning up your methods.

Upvotes: 2

elethan
elethan

Reputation: 16993

I just spent all day debugging a similar problem. In my case, the issue was as follows.

In one of my view functions I was using the Django send_mail() function. In my test, rather than having it send me an email every time I ran my tests, I patched send_mail in my test method:

from mock import patch
...

def test_stuff(self):
    ...

    with patch('django.core.mail.send_mail') as mocked_send_mail:

    ...

That way, after my view function is called, I can test that send_mail was called with:

self.assertTrue(mocked_send_mail.called)

This worked fine when running the test on its own, but failed when run with other tests in the suite. The reason this fails is that when it runs as part of the suite other views are called beforehand, causing the views.py file to be loaded, causing send_mail to be imported before I get the chance to patch it. So when send_mail gets called in my view, it is the actual send_mail that gets called, not my patched version. When I run the test alone, the function gets mocked before it is imported, so the patched version ends up getting imported when views.py is loaded. This situation is described in the mock documentation, which I had read a few times before, but now understand quite well after learning the hard way...

The solution was simple: instead of patching django.core.mail.send_mail I just patched the version that had already been imported in my views.py - myapp.views.send_mail. In other words:

with patch('myapp.views.send_mail') as mocked_send_mail:
...

This took me a long time to debug, so I thought I would share my solution. I hope it works for you too. You may not be using mocks, in which case this probably won't help you, but I hope it will help someone.

Upvotes: 3

rgilligan
rgilligan

Reputation: 804

Besides using TestCase for all your tests, you need to make sure you tear down any patching that was done in your setup methods:

def setUp(self):
    self.patcher = patch('my.app.module')

def tearDown(self):
    self.patcher.stop()

Upvotes: 2

Related Questions