我目前有一些单元测试,它们共享一组共同的测试。这是一个例子:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()
以上的输出是:
Calling BaseTest:testCommon
.Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 5 tests in 0.000s
OK
有没有办法重写上面的内容,以便不调用第一个testCommon
?
修改 我没有运行上面的5个测试,而是希望它只运行4个测试,2个来自SubTest1,另外2个来自SubTest2。似乎Python unittest自己运行原始的BaseTest,我需要一种机制来防止这种情况发生。
答案 0 :(得分:141)
使用多重继承,因此具有常见测试的类本身不会继承TestCase。
import unittest
class CommonTests(object):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(unittest.TestCase, CommonTests):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(unittest.TestCase, CommonTests):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()
答案 1 :(得分:110)
不要使用多重继承,它会咬你later。
相反,您可以将基类移动到单独的模块中,或者将其与空白类包装在一起:
import unittest
class BaseTestCases:
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTestCases.BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTestCases.BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()
输出:
Calling BaseTest:testCommon
.Calling SubTest1:testSub1
.Calling BaseTest:testCommon
.Calling SubTest2:testSub2
.
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK
答案 2 :(得分:30)
您可以使用一个命令解决此问题:
del(BaseTest)
所以代码看起来像这样:
import unittest
class BaseTest(unittest.TestCase):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
del(BaseTest)
if __name__ == '__main__':
unittest.main()
答案 3 :(得分:20)
Matthew Marshall的答案很棒,但它要求您在每个测试用例中继承两个类,这很容易出错。相反,我使用它(python> = 2.7):
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
if cls is BaseTest:
raise unittest.SkipTest("Skip BaseTest tests, it's a base class")
super(BaseTest, cls).setUpClass()
答案 4 :(得分:8)
你想要达到什么目的?如果你有通用的测试代码(断言,模板测试等),那么将它们放在没有test
前缀的方法中,这样unittest
就不会加载它们。
import unittest
class CommonTests(unittest.TestCase):
def common_assertion(self, foo, bar, baz):
# whatever common code
self.assertEqual(foo(bar), baz)
class BaseTest(CommonTests):
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(CommonTests):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(CommonTests):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()
答案 5 :(得分:6)
马修的答案是我需要使用的答案,因为我还在2.5。 但是从2.7开始,您可以在要跳过的任何测试方法上使用@ unittest.skip()装饰器。
http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures
您需要实现自己的跳过装饰器来检查基本类型。以前没有使用过这个功能,但是我可以使用BaseTest作为标记类型来调整跳过:
def skipBaseTest(obj):
if type(obj) is BaseTest:
return unittest.skip("BaseTest tests skipped")
return lambda func: func
答案 6 :(得分:4)
我想到解决这个问题的方法是在使用基类时隐藏测试方法。这样就不会跳过测试,因此在许多测试报告工具中测试结果可以是绿色而不是黄色。
与mixin方法相比,像PyCharm这样的思想不会抱怨基类中缺少单元测试方法。
如果基类继承自此类,则需要覆盖setUpClass
和tearDownClass
方法。
class BaseTest(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._test_methods = []
if cls is BaseTest:
for name in dir(cls):
if name.startswith('test') and callable(getattr(cls, name)):
cls._test_methods.append((name, getattr(cls, name)))
setattr(cls, name, lambda self: None)
@classmethod
def tearDownClass(cls):
if cls is BaseTest:
for name, method in cls._test_methods:
setattr(cls, name, method)
cls._test_methods = []
答案 7 :(得分:4)
另一个选择是不执行
unittest.main()
而不是你可以使用
suite = unittest.TestLoader().loadTestsFromTestCase(TestClass)
unittest.TextTestRunner(verbosity=2).run(suite)
所以你只在类TestClass
答案 8 :(得分:1)
我做的与@Vladim P.(https://stackoverflow.com/a/25695512/2451329)大致相同,但略有修改:
import unittest2
from some_module import func1, func2
def make_base_class(func):
class Base(unittest2.TestCase):
def test_common1(self):
print("in test_common1")
self.assertTrue(func())
def test_common2(self):
print("in test_common1")
self.assertFalse(func(42))
return Base
class A(make_base_class(func1)):
pass
class B(make_base_class(func2)):
def test_func2_with_no_arg_return_bar(self):
self.assertEqual("bar", func2())
然后我们去了。
答案 9 :(得分:0)
只需将testCommon方法重命名为其他方法即可。 Unittest(通常)会跳过任何没有'test'的东西。
快速而简单
import unittest
class BaseTest(unittest.TestCase):
def methodCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()`
答案 10 :(得分:0)
所以这是一个老线程,但我今天遇到了这个问题并想到了我自己的黑客。它使用一个装饰器,当通过基类进行访问时,它使函数的值为None。不需要担心setup和setupclass,因为如果基类没有测试,它们就不会运行。
compression resistance
答案 11 :(得分:0)
您可以在BaseTest类中添加__test_ = False
,但是如果添加它,请注意必须在派生类中添加__test__ = True
才能运行测试。
import unittest
class BaseTest(unittest.TestCase):
__test__ = False
def testCommon(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
__test__ = True
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
__test__ = True
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
if __name__ == '__main__':
unittest.main()
答案 12 :(得分:0)
从Python 3.2开始,您可以向模块添加test_loader函数,以控制由测试发现机制找到哪些测试(如果有)。
例如,以下内容将仅加载原始发布者的SubTest1
和SubTest2
测试用例,而忽略Base
:
def load_tests(loader, standard_tests, pattern):
suite = TestSuite()
suite.addTests([SubTest1, SubTest2])
return suite
应该有可能遍历standard_tests
(包含找到的默认加载程序的测试的TestSuite
)并将除Base
之外的所有内容复制到suite
,但是TestSuite.__iter__
的嵌套性质使事情变得更加复杂。
答案 13 :(得分:0)
这是仅使用已记录的单元测试功能的解决方案,可避免在测试结果中出现“跳过”状态:
class BaseTest(unittest.TestCase):
def __init__(self, methodName='runTest'):
if self.__class__ is BaseTest:
# don't run these tests in the abstract base implementation
methodName = 'runNoTestsInBaseClass'
super().__init__(methodName)
def runNoTestsInBaseClass(self):
pass
def testCommon(self):
# everything else as in the original question
工作原理:根据unittest.TestCase
documentation,“每个TestCase实例将运行一个基本方法:名为methodName的方法。”默认的“ runTests”在类上运行所有test *方法,这就是TestCase实例正常工作的方式。但是,当在抽象基类本身中运行时,您可以使用不执行任何操作的方法来简单地覆盖该行为。
副作用是您的测试计数将增加一:runNoTestsInBaseClass“ test”在BaseClass上运行时被视为成功测试。
(如果您仍然在Python 2.7中也可以使用。只需将super()
更改为super(BaseTest, self)
。)
答案 14 :(得分:-2)
将BaseTest方法名称更改为setUp:
class BaseTest(unittest.TestCase):
def setUp(self):
print 'Calling BaseTest:testCommon'
value = 5
self.assertEquals(value, 5)
class SubTest1(BaseTest):
def testSub1(self):
print 'Calling SubTest1:testSub1'
sub = 3
self.assertEquals(sub, 3)
class SubTest2(BaseTest):
def testSub2(self):
print 'Calling SubTest2:testSub2'
sub = 4
self.assertEquals(sub, 4)
输出:
在0.000秒内进行2次测试
调用BaseTest:testCommon调用
SubTest1:testSub1调用
BaseTest:testCommon调用
SubTest2:testSub2
TestCase.setUp()
方法叫做 准备测试夹具。这是 在呼叫之前立即呼叫 测试方法;提出的任何例外 这种方法将被认为是一种 错误而不是测试失败。该 默认实现什么都不做。