Reputation: 4989
I have a test in Django 1.5 that passes in these conditions:
TestCase
is runBut it fails when the full test suite is run with python manage.py test
. Why might this be happening?
The aberrant test uses django.test.Client
to POST
some data to an endpoint, and then the test checks that an object was successfully updated. Could some other app be modifying the test client or the data itself?
I have tried some print debugging and I see all of the data being sent and received as expected. The specific failure is a does-not-exist exception that is raised when I try to fetch the to-be-updated object from the db. Strangely, in the exception handler itself, I can query for all objects of that type and see that the target object does in fact exist.
Edit:
My issue was resolved when I found that I was querying for the target object by id
and User
and not id
and UserProfile
, but it's still confusing to me that this would work in some cases but fail in others.
I also found that the test would fail with python manage.py test auth <myapp>
Upvotes: 36
Views: 10120
Reputation: 14255
Here's yet another thing that introduces inadvertent dependencies between test cases:
Using the low level cache API and forgetting to clear the cache between tests.
For example, if you have a cached query like below, the cached result
will persist between tests, unless you refresh it explicitly:
from django.core.cache import cache
MY_KEY = 'some key'
def my_cached_query(refresh=False):
result = cache.get(MY_KEY)
if result is None or refresh:
# perform some expensive query
result = ...
cache.set(MY_KEY, result, ...)
return result
To fix this you could either set refresh=True
in all tests, or e.g. call cache.delete()
in setUp()
, or use some clever cache versioning, or use the dummy cache (also see here) when testing.
Upvotes: 0
Reputation: 196
This was happening to me too.
When running the tests individually they passed, but running all tests with ./manage.py test
it failed.
The problem in my case is because I had some tests inheriting from unittest.TestCase
instead of from django.test.TestCase
, so some tests were failing because there were registers on the database from previous tests.
After making all tests inherit from django.test.TestCase
this problem has gone away.
I found the answer on https://stackoverflow.com/a/436795/6490637
Upvotes: 0
Reputation: 3322
So I first read @elethan's answer and went 'well this is certainly not my problem, I'm not patching anything'. But it turned out I was indeed patching a method in a different test suite, which stayed permanently patched for the rest of the time the tests were run.
I had something of this sort going on;
send_comment_published_signal_mock = MagicMock()
comment_published_signal.send = send_comment_published_signal_mock
You can see why this would be a problem if some things are not cleaned up after running the test suite. The solution in my case was to use the helpful with
to restrict the scope.
signal = 'authors.apps.comments.signals.comment_published_signal.send'
with patch(signal) as comment_published_signal_mock:
do_your_test_stuff()
This is the easy part though, after you know where to look. The guilty test could come from anywhere. The solution to this is to run the failing test and other tests together until you find the cause, and then again, progressively narrowing it down, module by module.
Something like;
./manage.py test A C.TestModel.test_problem
./manage.py test B C.TestModel.test_problem
./manage.py test D C.TestModel.test_problem
Then recursively, if for example B
is the problem child;
./manage.py test B.TestModel1 C.TestModel.test_problem
./manage.py test B.TestModel2 C.TestModel.test_problem
./manage.py test B.TestModel3 C.TestModel.test_problem
This answer gives a good explanation for all this.
This answer is in the context of django
, but can really apply to any python tests.
Good luck.
Upvotes: 1
Reputation: 8247
There is a lot of nondeterminism that can come from tests that involve the database.
For instance, most databases don't offer deterministic selects unless you do an order by. This leads to the strange behavior where when the stars align, the database returns things in a different order than you might expect, and tests that look like
result = pull_stuff_from_database()
assert result[0] == 1
assert result[1] == 2
will fail because result[0] == 2 and result[1] == 1
.
Another source of strange nondeterministic behavior is the id autoincrement together with sorting of some kind.
Let's say each tests creates two items and you sort by item name before you do assertions. When you run it by itself, "Item 1" and "Item 2" work fine and pass the test. However, when you run the entire suite, one of the tests generates "Item 9" and "Item 10". "Item 10" is sorted ahead of "Item 9" so your test fails because the order is flipped.
Upvotes: 1
Reputation: 1654
Another possibility is that you've disconnected signals in the setUp of a test class and did not re-connect in the tearDown. This explained my issue.
Upvotes: 2
Reputation: 16993
It sounds like your problem does not involve mocks, but I just spent all day debugging an issue with similar symptoms, and your question is the first one that came up when I was searching for a solution, so I wanted to share my solution here, just in case it will prove helpful for others. In my case, the issue was as follows.
I had a single test that would pass in isolation, but fail when run as part of my full test suite. 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 patch
ed 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:
...
Upvotes: 42
Reputation: 1617
Try this to help you debug:
./manage.py test --reverse
In my case I realised that one test was updating certain data which would cause the following test to fail.
Upvotes: 7