Python unittest TestCase具有继承性

com*_*pie 10 python inheritance unit-testing

目前我有许多类似的unittest TestCases.每个TestCase都包含数据(输入值+预期输出值)和逻辑(调用SUT并将实际输出与预期输出进行比较).

我想将数据与逻辑分开.因此,我想要一个只包含逻辑的基类和一个只包含数据的派生类.到目前为止我想出了这个:

import unittest

class MyClass():
    def __init__(self, input):
        self.input = input
    def get_result(self):
        return self.input * 2

class TestBase(unittest.TestCase):
    def check(self, input, expected_output):
        obj = self.class_under_test(input)
        actual_output = obj.get_result()
        self.assertEqual(actual_output, expected_output)

    def test_get_result(self):
        for value in self.values:
            self.check(value[0], value[1])

class TestMyClass(TestBase):
    def __init__(self, methodName='runTest'):
        unittest.TestCase.__init__(self, methodName)        
        self.class_under_test = MyClass
        self.values = [(1, 2), (3, 6)]

unittest.main(exit = False)
Run Code Online (Sandbox Code Playgroud)

但是这失败了以下错误:

AttributeError: 'TestBase' object has no attribute 'values'
Run Code Online (Sandbox Code Playgroud)

两个问题:

  • 我的"设计"有什么好处吗?
  • 让它运作还需要什么?

Gia*_*ini 10

这里有点晚,但最近开始需要单元测试继承

我能找到的最优雅的解决方案是:

首先 - 你需要一个基础测试类

class MyBaseUnitTest(unittest.TestCase):
    __test__ = False
    def test_someting(self):
        ...

    def test_something_else(self):
        ...
Run Code Online (Sandbox Code Playgroud)

然后继承该类并运行测试:

class TestA(MyBaseUnitTest):
    __test__ = True

    def test_feature(self):
        pass
    def test_feature2(self):
        pass
Run Code Online (Sandbox Code Playgroud)

这是拥有单个视图集继承的最佳且最简单的方法。

我发现多重继承的问题是,当您尝试调用方法时setUp(),不会在基测试类上调用它,因此您必须在您编写的每个扩展基类的类中调用它。

我希望这会在将来的某个地方帮助某人。

顺便说一句:这是在 python3 中完成的 - 我不知道它在 python2 中会如何反应

更新:

这可能更好,更pythonic

class MyBaseUnitTest(object):
    def test_someting(self):
        ...

    def test_something_else(self):
        ...

class TestA(MyBaseUnitTest, unittest.TestCase):

    def test_feature(self):
        pass
    def test_feature2(self):
        pass
Run Code Online (Sandbox Code Playgroud)

只要基础测试类不扩展“unittest.TestCase”,测试运行器就不会解析这些测试,它们也不会在套件中运行。它们只会在基类扩展它们的地方运行。

  • 我发现更新中建议的代码存在潜在问题。由于 mixin `MyBaseUnitTest` 不是从 `unittest.TestCase` 继承的,所以不能在其中调用 `self.assertEqual` 等。 (6认同)

Ben*_*ter 6

为了使这项工作符合预期,您至少需要:

  • 确保子类测试用例的init方法与TestCase 的init方法匹配,即__init__(self, methodName="runTest")
  • 在子类的init方法中添加一个超级调用,例如super(TestMyClass, self).__init__(methodName)
  • 为test_get_result添加一个self参数,即 def test_get_result(self):

至于它是否是好的设计,请记住,您的测试部分作为您的代码如何工作的文档.如果你已经将所有工作隐藏在TestCase实例状态中,那么它的作用就不那么明显了.你可能会更好,比如写一个mixin类,它有自定义断言,可以获取输入和预期输出.


mgi*_*son 5

这种设计(或多或少)很好-一个“打cup”是当unittest查看所有 TestCase类并在它们上运行以“ test”开头的方法时。此时,您有一些选择。

一种方法是指定被测和价值类作为属性。在这里,如果可能的话,您将希望这些值是不可变的...

class TestBase(unittest.TestCase):

    def check(self, input, expected_output):
        obj = self.class_under_test(input)
        actual_output = obj.get_result()
        self.assertEqual(actual_output, expected_output)

    def check_all(self):
        for value in self.values:
            self.check(value[0], value[1])

class TestMyClass1(TestBase):
    values = ((1, 2), (3, 4))
    class_under_test = MyClass1

    def test_it(self):
        self.check_all()

class TestMyClass2(TestBase):
    values = (('a', 'b'), ('d', 'e'))
    class_under_test = MyClass2

    def test_it(self):
        self.check_all()
Run Code Online (Sandbox Code Playgroud)