单元测试如何/为何在测试用例中相互影响以及如何防止这种行为?

Don*_*Don 6 django unit-testing python-3.x django-unittest

我正在为一个用 Python3.6/Django 2.0 制作的 web 应用程序编写测试,我有以下情况:

class TestFoo(TestCase):
    def test_a(self):
        obj = Foo.objects.create(a = "bar")
        expectation = {"a" : "bar"}
        self.assertEquals(obj.method_x(), expectation)

    def test_b(self):
        obj = Foo.objects.create(a = "baz")
        expectation = {"a" : "baz"}
        self.assertEquals(obj.method_x(), expectation)

    def test_c(self):
        obj = Foo.objects.create(a = "bar")
        obj2 = Foo.objects.create(a = "baz")
        obj.b.add(obj2)
        expectation = {"a" : "bar", "b" : {"a": "baz"}}
        self.assertEquals(obj.method_x(), expectation)
Run Code Online (Sandbox Code Playgroud)

据我了解,每个测试都是单独运行的,但是当我与测试 a 或 b 一起运行 test_c 时,所有测试都会失败。基本上是这样的:

  • test_a + test_b + test_c =全部失败
  • 测试_a + 测试_b =全部通过
  • test_c =全部通过
  • test_a + test_c =全部失败
  • test_b + test_c =全部失败

我努力了:

  1. 在拆卸中删除所有 Foo 对象(如果这没有发生),这没有效果
  2. 使用 patch.object,但这不是我想要的行为,因为我想测试该方法是否正常工作
  3. 将 test_c 放在单独的类中,这没有效果
  4. 按一定顺序运行测试(a,然后 b,然后 c,第一个 c,然后 a/b,这会导致不同的失败点;如果我首先运行 c,它会通过,然后 a/b 会失败。如果我运行先 a/b,然后 c 失败

我不确定是什么导致了这种行为,但想解决它;我知道所有测试本身都应该通过。我一直在阅读有关模拟/修补方法的内容,但我很确定这不是我需要的,因为我需要验证对象的方法是否返回有效数据,而不是确保它们被调用或类似的东西。

所以基本上我的问题是双重的:

  1. 为什么会发生这种情况?
  2. 我该如何预防?

编辑1: 断言错误回溯也很奇怪;显然这些值并不相等,但更重要的是这些值以某种方式混合在一起。不知何故 test_a.method_x() == test_c.method_x(),但 test_a.a =/= test_c.a

method_x 类似于:

def method_x(self):
    if not self.b:
        return {"a": self.a}
    else:
        return {"a": self.a, "b":{"a":self.b.a}}
Run Code Online (Sandbox Code Playgroud)

该模型看起来像:

class Foo(models.Model):
    A_TYPES = (
        ("bar", "Bar"),
        ("baz", "Baz")
    )
    a = models.CharFields(max_length20, choices=A_TYPES)
    b = models.ManyToManyField("self")
    c = models.IntegerField(null=True)
    d = models.BooleanField(default=False)
Run Code Online (Sandbox Code Playgroud)

Gri*_*ave 0

我今天遇到了测试相互干扰的问题。问题原来是缓存问题,如下所示:

/sf/answers/5015624211/

from django.core.cache import cache
from django.test import TestCase

class TheTestCase(TestCase):

    def tearDown(self):
        super().tearDown()
        cache.clear()
Run Code Online (Sandbox Code Playgroud)

或者您可以在测试期间禁用缓存。这可能会减慢其他测试的速度,因此您必须做出决定。