Issue
When I run tests in parallel, I get random failures because one test interferes with the cache of another test.
I can work around the problem with
@override_settings(
CACHES={
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "[random_string]",
}
},
)
Actually to make that smaller I created a @isolate_cache
decorator that is a wrapper around override_settings
.
But still I need to go and decorate a large number of test cases. This is error prone, because, as I said, the failures are random. I might run the test suite 100 times without error and think that everything is OK, but I still might have forgotten to decorate a test case, and at some point it will fail randomly.
I've also thought about creating my own TestCase
subclass and use only that for all my test cases. This presents a similar problem: at some point someone would inherit from django.test.TestCase
out of habit, and it might not fail for a long time. Besides, some of my tests inherit from rest_framework.test.APITestCase
(or other classes), so there isn't a single test case subclass.
Is there any way to tell Django to run each test case in an isolated section of the cache once and for all?
Solution
You don't need "an isolated section of the cache", just to clear cache between tests.
Here are a few ways.
1. Subclass TestCase
The question mentions this is not desired, but I should still mention this proper way.
from django.core.cache import cache
from django.test import TestCase
class CacheClearTestCase(TestCase):
def tearDown(self):
# super().tearDown()
cache.clear()
2. Patch TestCase.tearDown
Assuming subclasses that override tearDown
call super().tearDown()
, you could do this.
Add this in manage.py before execute_from_command_line(sys.argv)
:
if sys.argv[1] == 'test':
from django.test import TestCase
from django.core.cache import cache
TestCase.tearDown = cache.clear
3. Subclass TestSuite
You can clear the cache after each test by subclassing TestSuite
to override _removeTestAtIndex
and setting DiscoverRunner.test_suite
to that subclass.
Add this in manage.py before execute_from_command_line(sys.argv)
:
if sys.argv[1] == 'test':
from unittest import TestSuite
from django.core.cache import cache
from django.test.runner import DiscoverRunner
class CacheClearTestSuite(TestSuite):
def _removeTestAtIndex(self, index):
super()._removeTestAtIndex(index)
cache.clear()
DiscoverRunner.test_suite = CacheClearTestSuite
Why you don't need an isolated section of the cache
To be clear, this is not a problem caused by running tests in parallel.
From https://docs.djangoproject.com/en/4.0/ref/django-admin/#cmdoption-test-parallel:
--parallel [N]
Runs tests in separate parallel processes.
From https://docs.djangoproject.com/en/4.0/topics/cache/#local-memory-caching-1:
Note that each process will have its own private cache instance, which means no cross-process caching is possible.
Answered By - aaron
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.