并行运行测试时的 Django 缓存隔离

问题描述 投票:0回答:3

当我并行运行测试时,我会遇到随机失败,因为一个测试干扰了另一个测试的缓存。

我可以解决这个问题

@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 在缓存的隔离部分中一劳永逸地运行每个测试用例?

django django-testing
3个回答
8
投票

您不需要“缓存的隔离部分”,只需在测试之间清除缓存即可。

这里有一些方法。

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()
,你可以这样做。

将其添加到manage.py中

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.子类
TestSuite

您可以在每次测试后通过子类化

TestSuite
来覆盖
_removeTestAtIndex
并将
DiscoverRunner.test_suite
设置为该子类来清除缓存。

将其添加到manage.py中

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

为什么不需要缓存的隔离部分

需要明确的是,这不是由并行运行测试引起的问题。

来自 https://docs.djangoproject.com/en/4.0/ref/django-admin/#cmdoption-test-parallel

--parallel [N]

在单独的并行进程中运行测试。

来自 https://docs.djangoproject.com/en/4.0/topics/cache/#local-memory-caching-1

请注意,每个进程都有自己的私有缓存实例,这意味着不可能进行跨进程缓存。


0
投票

最简单的解决方案是为测试提供一个单独的设置文件,您可以在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 的东西。


0
投票

我认为如果您需要将测试方法彼此隔离,最好使用自定义上下文管理器,这是我正在使用的:

class SomeTest(TestCase):

    class cache_clearer(object):
        def __init__(self, _cache=None):
            self.cache = _cache or cache

        def __enter__(self):
            self.cache.clear()

        def __exit__(self, exc_type, exc_val, exc_tb):
            self.cache.clear()

    def test_something(self):
        with self.cache_clearer():
            cache.set('some', 'value', 60)
© www.soinside.com 2019 - 2024. All rights reserved.