使用python unittest抽象测试用例
是否可以创建一个抽象的使用python unittest抽象测试用例,python,unit-testing,testcase,Python,Unit Testing,Testcase,是否可以创建一个抽象的TestCase,它将有一些测试方法,但是不会调用这个TestCase,并且这些方法将只在子类中使用?我想我将在我的测试套件中有一个抽象的TestCase,它将为单个接口的几个不同实现子类化。这就是为什么所有的测试方法都是一些,只有一个,内部方法的变化。如何以优雅的方式完成它?如果您遵循在run\u unittest中显式列出所有测试类的约定(参见Python测试套件了解该约定的许多用法),那么不列出特定的类将是直接的 如果您想继续使用unittest.main,并且如果您
TestCase
,它将有一些测试方法,但是不会调用这个TestCase
,并且这些方法将只在子类中使用?我想我将在我的测试套件中有一个抽象的TestCase
,它将为单个接口的几个不同实现子类化。这就是为什么所有的测试方法都是一些,只有一个,内部方法的变化。如何以优雅的方式完成它?如果您遵循在run\u unittest中显式列出所有测试类的约定(参见Python测试套件了解该约定的许多用法),那么不列出特定的类将是直接的
如果您想继续使用unittest.main,并且如果您可以允许使用unittest2(例如,来自Python2.7),您可以使用它的协议来指定哪些类包含测试用例)。在早期版本中,您必须将TestLoader子类化并重写。我不太明白您打算做什么-- 经验法则是“不要对测试很聪明”—— 把它们放在那里,写得很清楚 但是为了实现您想要的,如果您从unittest.TestCase继承,那么无论何时调用unittest.main(),您的“抽象”类都将被执行——我认为这是您想要避免的情况 只要这样做: 创建从“对象”继承的“抽象”类,而不是从TestCase继承的类。 对于实际的“具体”实现,只需使用多重继承: 从unittest.TestCase和抽象类继承
import unittest
class Abstract(object):
def test_a(self):
print "Running for class", self.__class__
class Test(Abstract, unittest.TestCase):
pass
unittest.main()
更新:首先颠倒继承顺序-
抽象
,这样它的定义就不会被测试用例
默认值覆盖,下面的注释中也指出了这一点。多重继承在这里不是一个很好的选择,主要是因为以下两个原因:
TestCase
中没有一个方法使用super()
,因此您必须首先列出类,才能使用setUp()
和tearDown()等方法
self.assertEquals()
etc未在self
上定义run()
变成只对基类有效的no-op
类TestBase(unittest.TestCase):
定义初始化(self,*args,**kwargs):
super(TestBase,self)。\uuuuuu初始化(*args,**kwargs)
self.helper=无
#Kludge警报:我们希望这个类在不运行的情况下携带测试用例
#通过单元测试框架,因此“run”方法被重写
#没什么。但是为了让子类能够在
#调用run时,构造函数将从TestCase重新绑定“run”。
如果是self.\uuuuuu class\uuuuuu!=测试库:
#从父类重新绑定“run”。
self.run=unittest.TestCase.run.\uuuuu获取\uuuuuuu(self,self.\uuuuuu类)
其他:
self.run=lambda self,*args,**kwargs:None
def新助手(自我):
引发未实现的错误()
def设置(自):
打印“为所有子类共享”
self.helper=self.newHelper()
def testFoo(self):
打印“为所有子类共享”
#用self.helper测试某事
类Test1(TestBase):
def新助手(自我):
返回帮助对象1()
类Test2(TestBase):
def新助手(自我):
返回帮助对象2()
恕我直言,尽管这可能违反某些惯例,但您可以将抽象测试用例定义为受保护的成员,以防止其执行。我在Django中实现了以下内容,并按要求工作。见下面的例子
from django.test import TestCase
class _AbstractTestCase(TestCase):
"""
Abstract test case - should not be instantiated by the test runner.
"""
def test_1(self):
raise NotImplementedError()
def test_2(self):
raise NotImplementedError()
class TestCase1(_AbstractTestCase):
"""
This test case will pass and fail.
"""
def test_1(self):
self.assertEqual(1 + 1, 2)
class TestCase2(_AbstractTestCase):
"""
This test case will pass successfully.
"""
def test_1(self):
self.assertEqual(2 + 2, 4)
def test_2(self):
self.assertEqual(12 * 12, 144)
Python unittest库具有,可用于实现您想要的功能:
#使用AbstractTestCase类将此函数添加到模块中
def加载测试(加载程序、测试):
结果=[]
对于测试中的测试用例:
如果类型(test\u case.\u tests[0])是AbstractTestCase:
持续
result.append(测试用例)
返回加载器。suiteClass(结果)
到目前为止,每个人都错过了一个非常简单的方法。与一些答案不同的是,它适用于所有测试驱动程序,而不是在您切换它们时失败
只需像往常一样使用继承,然后添加:
del AbstractTestCase
如果您真的想使用继承而不是混合,一个简单的解决方案是将抽象测试嵌套在另一个类中 它避免了测试运行程序发现的问题,并且您仍然可以从另一个模块导入抽象测试
import unittest
class AbstractTests(object):
class AbstractTest(unittest.TestCase)
def test_a(self):
print "Running for class", self.__class__
class Test(AbstractTests.AbstractTest):
pass
想要做OP正在做的事情的另一个原因是创建一个高度参数化的基类,该基类实现了需要在多个环境/场景中重现的一组核心测试。我所描述的本质上是使用unittest创建一个参数化的fixture,一个la pytest 假设您(像我一样)决定尽可能快地逃离任何基于多继承的解决方案,那么使用load_tests()从加载的套件中筛选出基类可能会出现以下问题: 在标准TestLoader中,从类自动加载完成后调用load_测试。因为: *此自动从类加载将尝试使用标准签名init(self,name)和 *您可能希望这个基类具有非常不同的ctor签名,或者 *您可能希望跳过构建,然后出于其他原因删除基类实例 。。您可能希望完全防止从基类自动加载测试实例 编辑:是一种更优雅、简洁、独立的方式。我已经实现了“嵌套类技巧”,并确认它可以很好地防止TestLoader“找到”您的测试用例库 我最初是通过修改TestLoader.loadTestsFromModule来完成的,只需跳过
for name in dir(module):
obj = getattr(module, name)
# skip TestCase classes:
# 1. without any test methods defined
# 2. that are base classes
# (we don't allow instantiating TestCase base classes, which allows test designers
# to implement actual test methods in highly-parametrized base classes.)
if isinstance(obj, type) and issubclass(obj, unittest.TestCase) and \
self.getTestCaseNames(obj) and not isbase(obj, module):
loaded_suite = self.loadTestsFromTestCase(obj)
# ignore empty suites
if loaded_suite.countTestCases():
tests.append(loaded_suite)
def isbase(cls, module):
'''Returns True if cls is base class to any classes in module, else False.'''
for name in dir(module):
obj = getattr(module, name)
if obj is not cls and isinstance(obj, type) and issubclass(obj, cls):
return True
return False
class AbstractTest(TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def _test_1(self):
# your test case here
class ConcreteTest(AbstractTest)
def test_1(self):
self._test_1()
class BaseTestCase(unittest.TestCase):
@classmethod
def setUpClass(cls):
if cls is BaseTestCase:
raise unittest.SkipTest("%s is an abstract base class" % cls.__name__)
else:
super(BaseTestCase, cls).setUpClass()