在对TestCase进行子类化时,如何防止Django在父类上运行单元测试



背景:我正在开发一个web scraper来跟踪网上商店的价格。它使用Django。我为每个商店都有一个模块,为每个商店编写get_price()get_product_name()等功能,以便主刮刀模块可以互换使用这些模块。我有store_a.py、store_b.py、store_c.py等,每个都定义了这些函数。

为了防止代码重复,我制作了StoreTestCase,它继承了TestCase。对于每个商店,我都有一个StoreTestCase的子类,比如StoreATestCase和StoreBTestCase。

当我手动测试StoreATestCase时,测试运行程序会执行我想要的操作。它使用子类self.data中的数据进行测试,并且不尝试单独设置和测试父类:

python manage.py test myproject.tests.test_store_a.StoreATest

然而,当我手动测试模块时,比如:

python manage.py test myproject.tests.test_store_a

它首先为子类运行测试并成功,但随后为父类运行测试,并返回以下错误:

for page in self.data:
TypeError: 'NoneType' object is not iterable

store_test.py(父类)

from django.test import TestCase
class StoreTestCase(TestCase):
def setUp(self):
'''This should never execute but it does when I test test_store_a'''
self.data = None
def test_get_price(self):
for page in self.data:
self.assertEqual(store_a.get_price(page['url']), page['expected_price'])

testrongtore_a.py(子类)

import store_a
from store_test import StoreTestCase
class StoreATestCase(StoreTestCase):
def setUp(self):
self.data = [{'url': 'http://www.foo.com/bar', 'expected_price': 7.99},
{'url': 'http://www.foo.com/baz', 'expected_price': 12.67}]

如何确保Django测试运行程序只测试子类,而不测试父类?

解决此问题的一种方法是使用Mixins:

from django.test import TestCase
class StoreTestCase(object):
def setUp(self):
'''This should never execute but it does when I test test_store_a'''
self.data = None
def test_get_price(self):
for page in self.data:
self.assertEqual(store_a.get_price(page['url']), page['expected_price'])
class StoreATestCase(StoreTestCase, TestCase):
def setUp(self):
self.data = [{'url': 'http://www.foo.com/bar', 'expected_price': 7.99},
{'url': 'http://www.foo.com/baz', 'expected_price': 12.67}]

StoreTestCase不会被执行,因为它不是TestCase,但您的StoreATestCase仍将从继承中受益。

我认为发生您的问题是因为StoreTestCaseTestCase实例,所以在运行测试时会执行它。

编辑:

我还建议在StoreTestCase.setUp中提出一个异常,明确表示未实现。看看这些例外。你最终会得到这样的东西:

import exceptions  # At the top of the file
[...]
def setUp(object):
raise exceptions.NotImplementedError('Please override this method in your subclass')

您可以将基类隐藏在另一个:中

store_test.py(父类)

from django.test import TestCase
class TestHelpers(object):
class StoreTestCase(TestCase):
...

testrongtore_a.py(子类)

import store_a
from store_test import TestHelpers
class StoreATestCase(TestHelpers.StoreTestCase):
...

如果您想避免多重继承,这也是一个可能的解决方案。Django测试用例不是通过init构造函数调用的,因此必须重写setUp方法:

from unittest import SkipTest
from django.test import TestCase
class BaseTest(TestCase):
def setUp(self):
if self.__class__ == BaseTest:
raise SkipTest('Abstract test')
your_stuff = 'here'
...

唯一的缺点是,跳过的测试将在测试报告中提及。单元测试文档:https://docs.python.org/dev/library/unittest.html#unittest.SkipTest

最新更新