如何向一组Django TestCase派生类添加测试方法?
我有一组测试用例,它们都需要进行完全相同的测试,比如“方法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 个回答
你可以使用一个混合类,利用测试运行器只会运行那些继承自 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 方法解析顺序”和“多重继承”。
我遇到过类似的问题。我无法阻止基类中的测试方法被执行,但我确保它不会实际运行任何代码。我是通过检查一个属性来做到这一点,如果这个属性被设置了,就立即返回。这个属性只在基类中设置,因此测试在其他地方都能运行,但在基类中不会。
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):
...