【问题标题】:How to unittest every class with the same public API in a module with python?如何使用 python 在模块中使用相同的公共 API 对每个类进行单元测试?
【发布时间】:2013-05-16 15:17:06
【问题描述】:

我有一个模块可以解析不同的文件格式,如 CSV、XLS、HTML 等。
不同的格式包含完全相同的信息,所以我制作了不同的类来解析不同的文件类型。这些类具有完全相同的公共 API,并且显然具有相同的基类,如下所示:

class BaseParser(object):
    def countTotalValues(self):
        pass

    def countItems(self):
        pass

class CSVParser(BaseParser):
    def __init__(self):
        """Init data, check for errors, etc.."""

    def parse(self):
        """ parses the data and set the result dictionaries like self.values, self.items, etc."""

class XLSParser(BaseParser):
    """ Ha exactly the same public API as CSVParser and set the same inner values. """
    pass

用相同的值一次对每个类进行单元测试是个好主意吗? 我一直在这样做:

class TestParserClasses(unittest.TestCase):
    def setUp(self):
        self.instances = []
        for class_, file_ in PARSER_CLASSES:
            self.instances.append(class_(file_))

    def tearDown(self):
        del self.instances

并以这种方式定义每个单元测试:

    def test_count_total_values_without_parameter(self):
        for parser in self.instances:
            parser.parse()
            self.assertEqual(Decimal('9216.84'), parser.countTotalValues())

    def test_count_items_without_parameter(self):
        for parser in self.instances:
            parser.parse()
            self.assertEqual(128, parser.countItems())

这样做可以吗?如果是,如何在不使用相同的 TestCases 的情况下一次对所有这些进行单元测试

    for parser in self.instances:
        parser.parse()

所以我想这样写单元测试:

def test_count_total_values_without_parameter(self):
    self.assertEqual(Decimal('9216.84'), parser.countTotalValues())

def test_count_items_without_parameter(self):
    self.assertEqual(128, parser.countItems())

适用于每个类。有可能吗?

【问题讨论】:

标签: python unit-testing python-unittest


【解决方案1】:

您是否考虑过创建一个基础 TestCase 并为您希望测试的每个类对其进行子类化?

像这样创建它,包含您的实际测试方法,并将其保存在与测试模块模式 (by default, test*.py) 不匹配的单独模块中,以便 unittest 不会发现它:

import unittest

class BaseTestCase(unittest.TestCase):
    def test_count_total_values_without_parameter(self):
        self.assertEqual(Decimal('9216.84'), self.parser.countTotalValues())

    # …more tests…

现在,在您的测试模块中,执行以下操作:

import basetest

class TestCSVParser(basetest.BaseTestCase):
    def setUp(self):
        self.parser = CSVParser()

class TestXLSParser(basetest.BaseTestCase):
    def setUp(self):
        self.parser = XLSParser()

【讨论】:

  • 这将运行三个测试:BaseTestCase.test_count...TestCSVParser.test_count...TestXLSParser.test_count...,因为BaseTestCase 本身会被自动检测为测试用例。简单的补救方法是编写一个带有test_count_... 函数的mixin 类,并使用来自unittest.TestCasebaseTestCase 的多重继承。
  • @UlrichEckhardt 这就是我将BaseTestCase 保留在单独模块中的原因。在发布我的帖子之前,我创建了一个更简单的版本来验证,python -m unittest discover 没有选择basetest 模块;只有 test 模块中的子测试用例被选中,因为它与 test*.py glob 匹配(参见 docs.python.org/2/library/unittest.html#test-discovery
  • 有了@UlrichEckhardt 的建议,太好了!
  • 我根据@UlrichEckhardt 的建议更新了答案,如果您批准我的编辑,我将接受它。
  • @Walkman 谢谢你,但我建议你继续用你喜欢的解决方案写下你自己的答案并接受它。肯定有多种方法可以做到这一点。
猜你喜欢
  • 2012-09-20
  • 2022-10-18
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-19
  • 1970-01-01
相关资源
最近更新 更多