我目前有一些单元测试,它们共享一组共同的测试.这是一个例子:
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,我需要一种机制来防止这种情况发生.
使用多重继承,因此具有常见测试的类本身不会继承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()
不要使用多重继承,它会在以后咬你.
相反,您可以将基类移动到单独的模块中或使用空白类包装它:
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
您可以使用一个命令解决此问题:
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()
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()
你想要实现什么目标?如果你有通用的测试代码(断言,模板测试等),那么将它们放在没有前缀的方法中,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()
马修的答案是我需要使用的答案,因为我还在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
我想解决此问题的一种方法是通过隐藏测试方法(如果使用了基类)。这样就不会跳过测试,因此在许多测试报告工具中,测试结果可以是绿色而不是黄色。
与mixin方法相比,像PyCharm这样的ide不会抱怨基类中缺少单元测试方法。
如果基类从该类继承,则它将需要重写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 = []