如何向一组Django TestCase派生类添加测试方法?

16 投票
2 回答
5861 浏览
提问于 2025-04-16 03:30

我有一组测试用例,它们都需要进行完全相同的测试,比如“方法x是否返回一个存在的文件名?”

我认为最好的办法是创建一个基类,所有的测试用例都从这个基类继承,然后把测试放到这个类里。可惜的是,测试框架还是会尝试在基类上运行这个测试,这样做就没有意义了。

class SharedTest(TestCase):
    def x(self):
        ...do test...

class OneTestCase(SharedTest):
    ...my tests are performed, and 'SharedTest.x()'...

我试着加了一个检查,如果是在基类的对象上调用测试,就跳过这个测试,而不是在派生类上调用,像这样:

    class SharedTest(TestCase):
        def x(self):
            if type(self) != type(SharedTest()):
                ...do test...
            else:
                pass

但是我遇到了这个错误:

ValueError: no such test method in <class 'tests.SharedTest'>: runTest

首先,我希望能得到一些优雅的建议来解决这个问题。其次,虽然我不太想用type()这个方法,但我想理解为什么它不管用。

2 个回答

33

你可以使用一个混合类,利用测试运行器只会运行那些继承自 unittest.TestCase 的测试(而Django的 TestCase 就是继承自这个类的)。举个例子:

class SharedTestMixin(object):
    # This class will not be executed by the test runner (it inherits from object, not unittest.TestCase.
    # If it did, assertEquals would fail , as it is not a method that exists in `object`
    def test_common(self):
         self.assertEquals(1, 1)


class TestOne(TestCase, SharedTestMixin):
    def test_something(self):
         pass

    # test_common is also run

class TestTwo(TestCase, SharedTestMixin):
    def test_another_thing(self):
        pass

    # test_common is also run

想了解更多为什么这样做有效,可以搜索一下“python 方法解析顺序”和“多重继承”。

5

我遇到过类似的问题。我无法阻止基类中的测试方法被执行,但我确保它不会实际运行任何代码。我是通过检查一个属性来做到这一点,如果这个属性被设置了,就立即返回。这个属性只在基类中设置,因此测试在其他地方都能运行,但在基类中不会。

class SharedTest(TestCase):
    def setUp(self):
        self.do_not_run = True

    def test_foo(self):
        if getattr(self, 'do_not_run', False):
            return
        # Rest of the test body.

class OneTestCase(SharedTest):
    def setUp(self):
        super(OneTestCase, self).setUp()
        self.do_not_run = False

这有点像是个小技巧。可能还有更好的方法来解决这个问题,但我不太确定是什么。

更新

正如sdolan 所说,使用混入类是正确的做法。我怎么之前没想到呢?

更新 2

(在阅读评论后)如果(1)超类的方法能避免那种小技巧的 if getattr(self, 'do_not_run', False): 检查;(2)测试的数量能被准确统计,那就太好了。

有一种可能的方法可以做到这一点。Django会自动找到并执行所有在 tests 中的测试类,无论是 tests.py 还是一个同名的包。如果测试的超类被声明在测试模块 外部,那么就不会发生这种情况。它仍然可以被测试类继承。例如 SharedTest 可以放在 app.utils 中,然后被测试用例使用。这将是上述解决方案的一个更清晰的版本。

# module app.utils.test
class SharedTest(TestCase):
    def test_foo(self):
        # Rest of the test body.

# module app.tests
from app.utils import test
class OneTestCase(test.SharedTest):
    ...

撰写回答