运行 并行测试时的 Django 缓存隔离
Django cache isolation when running tests in parallel
当我 运行 并行测试时,我遇到了随机失败,因为一个测试干扰了另一个测试的缓存。
我可以解决这个问题
@override_settings(
CACHES={
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "[random_string]",
}
},
)
实际上为了使它更小,我创建了一个 @isolate_cache
装饰器,它是 override_settings
.
的包装器
但是我还需要去装饰大量的测试用例。这很容易出错,因为正如我所说,失败是随机的。我可能 运行 测试套件 100 次没有错误并认为一切正常,但我仍然可能忘记装饰测试用例,并且在某些时候它会随机失败。
我还考虑过创建自己的 TestCase
子类,并只将其用于我的所有测试用例。这提出了一个类似的问题:在某些时候有人会出于习惯从 django.test.TestCase
继承,并且它可能很长一段时间都不会失败。此外,我的一些测试继承自 rest_framework.test.APITestCase
(或其他 类),因此没有单个测试用例子类。
有没有什么办法告诉 Django 运行 每个测试用例都在缓存的一个隔离部分一劳永逸?
最简单的解决方案是拥有一个单独的测试设置文件,您可以在 manage.py 中加载该文件。这也可以导入所有默认设置。
manage.py
settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings)
test_settings.py
from .settings import * # import default settings
# setting overrides here
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "[random_string]",
}
}
如果您需要进行更多设置覆盖,尤其是对于多个环境,我建议使用类似 django-configurations.
的设置
您不需要“缓存的隔离部分”,只是为了在测试之间清除缓存。
这里有几种方法。
1。子类 TestCase
问题提到这是不希望的,但我还是应该提一下这个正确的方法。
from django.core.cache import cache
from django.test import TestCase
class CacheClearTestCase(TestCase):
def tearDown(self):
# super().tearDown()
cache.clear()
2。补丁 TestCase.tearDown
假设覆盖 tearDown
的子类调用 super().tearDown()
,您可以这样做。
在 execute_from_command_line(sys.argv)
之前的 manage.py 中添加:
if sys.argv[1] == 'test':
from django.test import TestCase
from django.core.cache import cache
TestCase.tearDown = cache.clear
3。子类 TestSuite
您可以在每次测试后清除缓存,方法是子类化 TestSuite
以覆盖 _removeTestAtIndex
并将 DiscoverRunner.test_suite
设置为该子类。
在 execute_from_command_line(sys.argv)
之前的 manage.py 中添加:
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
为什么不需要缓存的隔离部分
需要说明的是,这 不是 由 运行 并行测试引起的问题。
来自 https://docs.djangoproject.com/en/4.0/ref/django-admin/#cmdoption-test-parallel:
--parallel [N]
Runs tests in separate parallel processes.
来自 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.
当我 运行 并行测试时,我遇到了随机失败,因为一个测试干扰了另一个测试的缓存。
我可以解决这个问题
@override_settings(
CACHES={
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "[random_string]",
}
},
)
实际上为了使它更小,我创建了一个 @isolate_cache
装饰器,它是 override_settings
.
但是我还需要去装饰大量的测试用例。这很容易出错,因为正如我所说,失败是随机的。我可能 运行 测试套件 100 次没有错误并认为一切正常,但我仍然可能忘记装饰测试用例,并且在某些时候它会随机失败。
我还考虑过创建自己的 TestCase
子类,并只将其用于我的所有测试用例。这提出了一个类似的问题:在某些时候有人会出于习惯从 django.test.TestCase
继承,并且它可能很长一段时间都不会失败。此外,我的一些测试继承自 rest_framework.test.APITestCase
(或其他 类),因此没有单个测试用例子类。
有没有什么办法告诉 Django 运行 每个测试用例都在缓存的一个隔离部分一劳永逸?
最简单的解决方案是拥有一个单独的测试设置文件,您可以在 manage.py 中加载该文件。这也可以导入所有默认设置。
manage.py
settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'
os.environ.setdefault("DJANGO_SETTINGS_MODULE", settings)
test_settings.py
from .settings import * # import default settings
# setting overrides here
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "[random_string]",
}
}
如果您需要进行更多设置覆盖,尤其是对于多个环境,我建议使用类似 django-configurations.
的设置您不需要“缓存的隔离部分”,只是为了在测试之间清除缓存。
这里有几种方法。
1。子类 TestCase
问题提到这是不希望的,但我还是应该提一下这个正确的方法。
from django.core.cache import cache
from django.test import TestCase
class CacheClearTestCase(TestCase):
def tearDown(self):
# super().tearDown()
cache.clear()
2。补丁 TestCase.tearDown
假设覆盖 tearDown
的子类调用 super().tearDown()
,您可以这样做。
在 execute_from_command_line(sys.argv)
之前的 manage.py 中添加:
if sys.argv[1] == 'test':
from django.test import TestCase
from django.core.cache import cache
TestCase.tearDown = cache.clear
3。子类 TestSuite
您可以在每次测试后清除缓存,方法是子类化 TestSuite
以覆盖 _removeTestAtIndex
并将 DiscoverRunner.test_suite
设置为该子类。
在 execute_from_command_line(sys.argv)
之前的 manage.py 中添加:
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
为什么不需要缓存的隔离部分
需要说明的是,这 不是 由 运行 并行测试引起的问题。
来自 https://docs.djangoproject.com/en/4.0/ref/django-admin/#cmdoption-test-parallel:
--parallel [N]
Runs tests in separate parallel processes.
来自 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.