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

时间:2015-04-28 11:14:19

标签: python django unit-testing

背景:我正在使用网络抓取工具来跟踪在线商店的价格。它使用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'])

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测试运行器仅测试子类,而不测试父类?

3 个答案:

答案 0 :(得分:4)

解决此问题的一种方法是使用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')

答案 1 :(得分:0)

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

store_test.py (父类)

sys.path

test_store_a.py (子类)

import re as re, pandas as pd

from file2 import ClassB as ClassB
from file3 import ClassC as ClassC # ClassC contains static methods that use re
from file4 import ClassD as ClassD

class ClassA(object):
    """Converts dataframe from unicode string objects to Python
    string objects, renames column headers.
    """

    def __init__(self, dataframe): # dataframe is defined in file4.py in __main__
        super(ClassA, self).__init__()
        self.df = dataframe.replace({r'[^\x00-\x7F]+':''}, regex=True, inplace=True)
        self.df = dataframe.applymap(str)

    # I commented out the following and still receive the NameError.
    # for headerID in self.df.columns:
        # if ('UPC' or 'GTIN1' or 'GTIN' or 'EAN') in headerID:
           # header = headerID
           # self.df.rename(columns = {'%s'%header:'GTIN1'}, inplace = True)
        # if re.search(".MPN", headerID):
            # header = headerID
            # self.df.rename(columns = {'%s'%header:'MPN'}, inplace = True)

答案 2 :(得分:0)

如果您想避免多重继承,这也是一种可行的解决方案。不能通过 init 构造函数调用Django测试用例,因此必须重写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