【问题标题】:How do I prevent Django from running unit tests on parent class when subclassing TestCase?子类化TestCase时如何防止Django在父类上运行单元测试?
【发布时间】:2015-04-28 11:14:18
【问题描述】:

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

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

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

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

但是,当我手动测试 module 时,例如:

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'])

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}]

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

【问题讨论】:

  • 如果你不直接调用superStoreTestCase.__init__,它不应该执行它,因为它已经被覆盖了。

标签: python django unit-testing


【解决方案1】:

解决此问题的一种方法是使用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 仍将受益于继承。

我认为您的问题发生是因为 StoreTestCase 是一个 TestCase 实例,因此它会在您运行测试时执行。

编辑:

我还建议在StoreTestCase.setUp 中提出异常,明确表示未实现。看看these exception。 你最终会得到这样的结果:

import exceptions  # At the top of the file

[...]

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

【讨论】:

  • 在StoreTestCase(object)上做self.assertEqual()不是无效的吗,因为它没有那个方法?
  • 完全有效,它将使用来自StoreATestCaseassertEqual。查看答案中的链接(Mixins)。
【解决方案2】:

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

store_test.py(父类)

from django.test import TestCase

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

test_store_a.py(子类)

import store_a
from store_test import TestHelpers

class StoreATestCase(TestHelpers.StoreTestCase):
    ...

【讨论】:

    【解决方案3】:

    如果您想避免多重继承,这也是一种可能的解决方案。 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-01
      • 1970-01-01
      • 2013-09-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多