Python unittest 具有继承性的测试用例
Python unittest TestCase with inheritance
目前我有很多类似的unittest TestCases。每个 TestCase 都包含数据(输入值 + 预期输出值)和逻辑(调用 SUT 并将实际输出与预期输出进行比较)。
我想把数据和逻辑分开。因此,我想要一个仅包含逻辑的基 class 和一个仅包含数据的派生 class。到目前为止我想到了这个:
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)
但是失败并出现以下错误:
AttributeError: 'TestBase' object has no attribute 'values'
两个问题:
- 我的'design'好用吗?
- 它还需要什么才能正常工作?
要使这项工作按预期进行,您至少需要:
- 确保你的 subclasses 测试用例的 init 方法与 TestCase 匹配,即
__init__(self, methodName="runTest")
- 在子 class 的 init 方法中添加超级调用,例如
super(TestMyClass, self).__init__(methodName)
- 向 test_get_result 添加一个 self 参数,即
def test_get_result(self):
至于它是否是好的设计,请记住,您的测试在一定程度上充当了说明您的代码如何工作的文档。如果您将所有工作都隐藏在 TestCase 实例状态中,那么它所做的就不会那么明显。你可能会更好,比如说,编写一个 mixin class,它具有接受输入和预期输出的自定义断言。
您按了 test_get_result()
两次。我认为任何 test*()
方法都不应该在 TestBase
中。相反,使用 TestBase
提供自定义断言、错误格式化程序、测试数据生成器等,并将实际测试保留在 TestMyClass
.
中
设计(或多或少)不错——一个 "hiccup" 是当 unittest 查看 all TestCase classes 并运行以 "test" 开头的方法。此时您有几个选择。
一种方法是指定 class 被测和值作为 class 上的属性。在这里,如果可能的话,您会希望值是不可变的...
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()
Python 的 unittest.main()
执行当前命名空间中所有测试 class 的所有测试。
一种不运行直接在你的基础class中测试的方法是将它移动到不同的模块(例如将TestBase
移动到testbase.py
):
在调用 unittest.main()
的文件中,确保不要将 class 导入您的命名空间(不要使用 from testbase import *
或类似名称):
import testbase
class TestMyClass1(testbase.TestBase):
values = ((1, 2), (3, 4))
class_under_test = MyClass1
这里有点晚,但最近开始需要单元测试继承
我能找到的最优雅的解决方案是:
首先 - 您需要进行基础测试 class
class MyBaseUnitTest(unittest.TestCase):
__test__ = False
def test_someting(self):
...
def test_something_else(self):
...
然后继承 class 和 运行 测试:
class TestA(MyBaseUnitTest):
__test__ = True
def test_feature(self):
pass
def test_feature2(self):
pass
这是拥有单一视图集继承的最佳且简单的方法。
我发现多重继承的问题是,当你尝试调用像 setUp()
这样的方法时,它不会在基础测试 class 上调用,所以你必须在每个 class 你写的是扩展基数 class.
我希望这对将来某个地方的人有所帮助。
顺便说一句:这是在 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
只要基本测试 class 不扩展 "unittest.TestCase",测试 运行ner 就不会解决这些测试,它们也不会 运行 在套件中.它们只会 运行 在基础 class 扩展它们的地方。
目前我有很多类似的unittest TestCases。每个 TestCase 都包含数据(输入值 + 预期输出值)和逻辑(调用 SUT 并将实际输出与预期输出进行比较)。
我想把数据和逻辑分开。因此,我想要一个仅包含逻辑的基 class 和一个仅包含数据的派生 class。到目前为止我想到了这个:
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)
但是失败并出现以下错误:
AttributeError: 'TestBase' object has no attribute 'values'
两个问题:
- 我的'design'好用吗?
- 它还需要什么才能正常工作?
要使这项工作按预期进行,您至少需要:
- 确保你的 subclasses 测试用例的 init 方法与 TestCase 匹配,即
__init__(self, methodName="runTest")
- 在子 class 的 init 方法中添加超级调用,例如
super(TestMyClass, self).__init__(methodName)
- 向 test_get_result 添加一个 self 参数,即
def test_get_result(self):
至于它是否是好的设计,请记住,您的测试在一定程度上充当了说明您的代码如何工作的文档。如果您将所有工作都隐藏在 TestCase 实例状态中,那么它所做的就不会那么明显。你可能会更好,比如说,编写一个 mixin class,它具有接受输入和预期输出的自定义断言。
您按了 test_get_result()
两次。我认为任何 test*()
方法都不应该在 TestBase
中。相反,使用 TestBase
提供自定义断言、错误格式化程序、测试数据生成器等,并将实际测试保留在 TestMyClass
.
设计(或多或少)不错——一个 "hiccup" 是当 unittest 查看 all TestCase classes 并运行以 "test" 开头的方法。此时您有几个选择。
一种方法是指定 class 被测和值作为 class 上的属性。在这里,如果可能的话,您会希望值是不可变的...
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()
Python 的 unittest.main()
执行当前命名空间中所有测试 class 的所有测试。
一种不运行直接在你的基础class中测试的方法是将它移动到不同的模块(例如将TestBase
移动到testbase.py
):
在调用 unittest.main()
的文件中,确保不要将 class 导入您的命名空间(不要使用 from testbase import *
或类似名称):
import testbase
class TestMyClass1(testbase.TestBase):
values = ((1, 2), (3, 4))
class_under_test = MyClass1
这里有点晚,但最近开始需要单元测试继承
我能找到的最优雅的解决方案是:
首先 - 您需要进行基础测试 class
class MyBaseUnitTest(unittest.TestCase):
__test__ = False
def test_someting(self):
...
def test_something_else(self):
...
然后继承 class 和 运行 测试:
class TestA(MyBaseUnitTest):
__test__ = True
def test_feature(self):
pass
def test_feature2(self):
pass
这是拥有单一视图集继承的最佳且简单的方法。
我发现多重继承的问题是,当你尝试调用像 setUp()
这样的方法时,它不会在基础测试 class 上调用,所以你必须在每个 class 你写的是扩展基数 class.
我希望这对将来某个地方的人有所帮助。
顺便说一句:这是在 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
只要基本测试 class 不扩展 "unittest.TestCase",测试 运行ner 就不会解决这些测试,它们也不会 运行 在套件中.它们只会 运行 在基础 class 扩展它们的地方。