简体   繁体   English

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

[英]How do I prevent Django from running unit tests on parent class when subclassing TestCase?

Background: I am working on a web scraper to track prices at online stores.背景:我正在开发一个网络爬虫来跟踪在线商店的价格。 It uses Django.它使用 Django。 I have a module for each store, with functions like get_price() and get_product_name() written for each one, so that the modules can be used interchangeably by the main scraper module.我为每个商店都有一个模块,为每个商店编写了诸如get_price()get_product_name()类的函数,以便主刮板模块可以互换使用这些模块。 I have store_a.py, store_b.py, store_c.py, et cetera, each with these functions defined.我有 store_a.py、store_b.py、store_c.py 等等,每个都定义了这些函数。

In order to prevent duplication of code, I've made StoreTestCase, which inherits from TestCase.为了防止代码重复,我制作了 StoreTestCase,它继承自 TestCase。 For each store, I have a subclass of StoreTestCase, like StoreATestCase and StoreBTestCase.对于每个商店,我都有一个 StoreTestCase 的子类,例如 StoreATestCase 和 StoreBTestCase。

When I manually test the StoreATestCase class , the test runner does what I want.当我手动测试 StoreATestCase类时,测试运行器会执行我想要的操作。 It uses the data in the child class self.data for its tests, and doesn't attempt to set up and test the parent class on its own:它使用子类self.data的数据进行测试,并且不会尝试自行设置和测试父类:

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

However, when I manually test against the module , like:但是,当我手动测试模块时,例如:

python manage.py test myproject.tests.test_store_a

It first runs the tests for the child class and succeeds, but then it runs them for the parent class and returns the following error:它首先为子类运行测试并成功,但随后为父类运行它们并返回以下错误:

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

store_test.py (parent class) 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'])

test_store_a.py (child class) test_store_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}]

How do I ensure the Django test runner only tests the child class, and not the parent class?我如何确保 Django 测试运行器只测试子类,而不是父类?

One way to fix this is to use Mixins :解决此问题的一种方法是使用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}]

The StoreTestCase will not be executed since it is not a TestCase , but your StoreATestCase will still benefit from the inheritance. StoreTestCase不会被执行,因为它不是一个TestCase ,但你的StoreATestCase仍然会从继承中受益。

I think that your issue happens because StoreTestCase is a TestCase instance, so it gets executed when you run the tests.我认为你的问题发生是因为StoreTestCase是一个TestCase实例,所以当你运行测试时它会被执行。

Edit:编辑:

I would also suggest to raise an exception in StoreTestCase.setUp , explicitly saying that is not implemented.我还建议在StoreTestCase.setUp引发异常,明确表示未实现。 Have a look at these exception .看看这些异常 You would end up with something like this:你最终会得到这样的结果:

import exceptions  # At the top of the file

[...]

def setUp(object):
    raise exceptions.NotImplementedError('Please override this method in your subclass')

You can to hide base class inside another:您可以将基类隐藏在另一个中:

store_test.py (parent class) store_test.py (父类)

from django.test import TestCase

class TestHelpers(object):
    class StoreTestCase(TestCase):
    ...

test_store_a.py (child class) test_store_a.py (子类)

import store_a
from store_test import TestHelpers

class StoreATestCase(TestHelpers.StoreTestCase):
    ...

If you want to avoid the Multi-inheritance, this is a possible solution as well.如果您想避免多重继承,这也是一个可能的解决方案。 Django Test Cases are not called via the init constructor so the setUp Method has to be overridden: 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'
...

The only downside is, that the skipped test will be mentioned in your test report.唯一的缺点是,您的测试报告中会提到跳过的测试。 Unittest documentation: https://docs.python.org/dev/library/unittest.html#unittest.SkipTest单元测试文档: https ://docs.python.org/dev/library/unittest.html#unittest.SkipTest

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM