Unittest
一、unittest简介
可查看官方文档:https://docs.python.org/zh-cn/3/library/unittest.html#module-unittest
unittest是受Java中的JUnit的启发,针对于Python语言而推出的一种单元测试框架。有如下一些优点:
1、支持测试自动化,配置共享和机关代码测试
2、支持将测试样例聚合到测试集中,并将测试与报告框架独立。一般会调用测试函数,调用测试函数可以聚合所有的测试结果并产生结果报告
3、支持面向对象概念,有四个重要概念:测试脚手架(test fixture)、测试用例(test case)、
测试套件(test suit)、测试运行器(test runner)
测试脚手架:表示为了开展一箱货多项测试所需要进行的准备工作,以及所有相关的清理操作,每个测试用例运行时都会调用一次setUp()、tearDown()、init()
测试用例:一个独立的测试单元,检查输入特定的数据时的响应,需要继承unittest提供的基类:TestCase
测试套件:一系列的测试用例或者测试套件,或者测试用例与测试套件的混合,用于归档需要一起执行的测试
测试运行器:用于执行和输出测试结果的组件
二、unittest的基本使用
命名规则
方法的命名都以test开头,这个命名约定告诉测试运行者类的那些方法表示测试
setup和tearDown
def setUp():
# 每个测试用例运行前运行
...
def tearDown():
# 每个测试用例运行后运行
...
@classmethod
def setUpClass(cls):
# 在一个类中的所有用例执行之前运行的
...
@classmethod
def tearDownClass(cls):
# 在一个类中的所有用例都执行完之后运行的
函数调用
检查预期的输出:assertEqual()
验证条件:assertTrue()、assertFalse()、assertAlmostEqual()、
assertAlmostEquals()、assertCountEqual()、assertDictContainsSubset()、
assertDictEqual()、assertGreater()、assertGreaterEqual()、assertIn()、assertIs()、
assertIsInstance()、assertIsNone()、assertIsNot()、assertIsNotNone()、assertLess()、
assertLessEqual()、assertListEqual()、assertLogs()、assertMultiLineEqual()、assertNotAlmostEqual()、
assertNotAlmostEquals()、assertNotEqual()、assertNotEquals()、assertNotIn()、assertNotIsInstance()、
assertNotRegex()、assertNotRegexpMatches()、assertRegex()、assertRegexpMatches()、assertSequenceEqual()、
assertSetEqual()、assertTupleEqual()、
抛出异常:assertRaiseRegex()、assertRaiseRegexp()、assertWarns()、assertWarnsRegex()
命令行接口
通过主函数运行测试用例:
if __name__ == '__main__':
unittest.main()
在终端直接调用测试用例:
运行独立模块:
python -m unittest test_module01
运行测试类:
python -m unittest test_module01.testClass01
运行某个测试类下的测试方法:
python -m unittest test_module01.testClass01.test_01
通过文件路径指定:
python -m unittest test_module01/testClass01.py
探索性测试:
-v : 更详细地输出结果
-s : 开始进行搜索的目录(默认值为当前目录)
-p : 用于匹配测试文件的模式
-t : 指定项目的最上层目录(通常为开始时所在目录)
eg: 运行test_module01中的所有以_o1结尾的测试用例:
pyton -m unittest -s test_module01 -p *_o1.py
python -m unittest -s test_module01.testClass01 -p *_01.py
测试套件
自定义测试套件:
fp=file(filename,'wb')
def suite(self):
# 构造测试集,实例化测试套件
suit = unittest.TestSuite()
# 将测试用例加载到测试套件中,执行顺序是安装加载顺序:先执行test_02,再执行test_01
suit.addTest(Test('test_02'))
suit.addTest(Test('test_01'))
return suite
if __name__ == '__main__':
# unittest.main('-v')
# 实例化TextTestRunner类
# 可导入HTMLTestRunner包输出更详细的报告 runner=HTMLTestRunner.HTMLTestRunner(stream=fp,title=u'Atest测试用例执行',description=u'测试结果:') 结尾需要fp.close()
runner = unittest.TextTestRunner()
# 运行测试套件(运行测试套件中的所有用例
runner.run(suite())
构造测试集:执行顺序是命名顺序
test_dir = './'
discover = unittest.defaultTestLoader.discover(test_dir,pattern='test_*.py')
# 执行测试用例,实例化TextTestRunner类
runner = unittest.TextTestRunner()
# 运行测试套件
runner.run(discover)
通过模块加载测试用例:
suit = unittest.TestSuite()
# 实例化用力加载器
loader = unittest.TestLoader()
# 添加测试模块
suite.addTest(loader.loadTestsFromeModule(测试模块名))
通过测试类加载测试用例:
suit = unittest.TestSuite()
# 实例化用力加载器
loader = unittest.TestLoader()
# 添加测试模块
suite.addTest(loader.loadTestsFromeTestCase测试模块名))
一个一个添加测试用例:
suite = unittest.TestSuite()
suite.addTest(测试类名('方法名'))
复用已有的测试代码
快速将现有的测试转换成基于unittest的测试—FunctionTestCase类(不推荐此方法)
# 采用FunctionTestCase类复用已有测试代码
def testSomething():
something = makeSomethingValue()
assert something is not None
def makeSomethingValue():
something = 'test'
return something
def initSomething():
something = ''
testcase = unittest.FunctionTestCase(testSomething(),setUp=makeSomethingValue,tearDown=initSomething)
if __name__ == '__main__':
runner = unittest.TextTestRunner()
runner.run(testcase)
跳过测试与预计失败
跳过测试适用场景:a、用例正在开发中,需要先执行其它用例 b、用例已经废弃,但是需要存档 c、某些条件下不需要执行该用例
预计失败适用场景:a、一些执行失败被认为通过测试的用例
三种跳过测试装饰器:
@unittest.skip(‘reason’) --------->无条件跳过测试,reason为跳过的理由
@unittest.skipif(‘condition’,‘reason’) ----->当condition=True时跳过测试,reason为跳过的理由
@unittest.skipunless(‘condition’,‘reason’) —>当condition=False时跳过测试,reason为跳过的理由
一种预计失败装饰器:
@unittest.expectedFailure()
# @unittest.skip('跳过测试类')
class TestSkipCase(unittest.TestCase):
# @unittest.skip(reason): 无条件跳过测试,reason描述为什么跳过测试
@unittest.skip('tiaoguo')
def test_nothing(self):
self.fail("shouldn't happen !")
@unittest.skipIf(1==2,'equal')
def test_equal(self):
pass
@unittest.skipUnless(1==2,'dont equal')
def tast_dont_equal(self):
pass
def test_myskip(self):
if 1==2:
self.skipTest('1 != 2')
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1,2,"1 !=2")
@unittest.expectedFailure
def test_fail(self):
self.assertEqual(1, 1, "1==2")
if __name__ == '__main__':
unittest.run(['-v','-s'],TestSkipCase)
子测试区分测试迭代
使用subTest(),输出每次迭代更详细的错误信息
class NumbersTest(unittest.TestCase):
# 使用子测试
def test_even(self):
for i in range(0, 6):
with self.subTest(i=i):
self.assertEqual(i % 2, 0)
# 未使用子测试
def test_even02(self):
for i in range(0, 6):
self.assertEqual(i % 2, 0)
输出报告
HTMLTestRunner报告:
import HTMLTestRunner
#...
# 以二进制模式打开一个文件
f = open('test.html','wb')
runner = HTMLTestRunner.HTMLTestRunner(f,title='unittest用例标题',description='这是用例描述')
# 运行用例(用例集合)
runner.run(suite)
f.close()
BeautifulReport报告:
from BeautifulReport import BeautifulReport as bf
# ...
# 实例化BeautifulReport模块
run = bf(suite)
run.report(filename='test',description='这个描述参数是必填的')
数据驱动(DDT)
简介:测试数据参数化
应用场景:一般进行接口测试时,每个接口的传参都不止一种情况,一般会考虑正向、逆向等多种组合。所以在测试一个接口时通常会编写多条case,而这些case除了传参不同外,其实并没什么区别。
这个时候就可以利用ddt来管理测试数据,提高代码复用率
安装:pip install ddt
四种引入模式:a、引入装饰器@ddt b、导入数据的@data c、拆分数据的@unpack d、导入外部数据的@file_data
@ddt
class TestDDT01(unittest.TestCase):
# ====================读取元组======================
# 单组元素
@data(1,2,3)
def test01(self,value):
print(value)
# 未拆分多组元素
@data((1,2,3),(4,5,6))
def test02(self,value):
print(value)
# 拆分多组元素
@data((1,2,3),(4,5,6))
@unpack
def test03(self,value1,value2,value3):
print(value1,value2,value3)
# ==================读取列表=========================
# 多组元素未分解
@data([{'name':'hahah','age':12},{'name':'heheh','age':18}])
def test04(self,value):
print(value)
# 多组元素分解
@data([{'name':'hahah','age':12},{'name':'heheh','age':18}])
@unpack
def test05(self,value1,value2):
print(value1,value2)
# ===================读取字典数========================
# 多组元素未分解
@data({'name': 'hahah', 'age': 12}, {'name': 'heheh', 'age': 18})
def test06(self, value):
print(value)
# 多组数据拆分
@data({'name': 'hahah', 'age': 12}, {'name': 'heheh', 'age': 18})
@unpack
# 在拆分的时候,形参和实参的key值要一致,否则就报错
def test07(self, name, age):
# 结果展示的数据是字典里的value,不会打印key的值
print(name,age)
# ===================读取文件数据========================
testdata = [{'a': 'lili', 'b': 12}, {'a': 'sasa', 'b': 66}]
@data(*testdata)
# @unpack
def test08(self, value):
print(value)
@file_data('yaml/login.yaml')
def test10(self,**kwargs):
print(kwargs.get('url'))
三、unittest的API简介
断言函数
-
assertEqual(a,b,msg=None) 判断 a == b
-
assertNotqual(a,b,msg=None) 判断 a!=b
-
assertTrue(x,msg=None) 判断 bool(x) = True
-
assertFlase(x,msg=None) 判断 bool(x) = Flase
-
assertIs(a,b,msg=None) 判断 a is b , 包括对象
-
assertIsNone(x,msg=None) 判断 x is None
-
assertIsNotNone(x,msg=None) 判断 x is not None
-
assertIn(a,b,msg=None) 判断 a in b
-
assertNotIn(a,b,msg=None) 判断 a not in b
-
assertIsInstance(a,b,msg=None) 判断 isinstance(a,b)
-
assertNotIsInstance(a,b,msg=None) 判断(对象类型) not isinstance(a,b)
-
assertRaises(exc,fun,*args,**kwds) 判断fun(*args,*kwds) raises exc
解析:测试在使用任何位置或关键字参数调用callable时是否引发异常,这些参数也传递给assertRaises()。
如果引发异常,则测试通过;如果引发另一个异常,则测试出错;如果未引发异常,则测试失败。要捕获一组异常中的任何一个,
可以将包含异常类的元组作为异常传递 -
assertRaises(exception,msg=None) 返回一个上下文管理器
解析:如果只给出了异常和可能的msg参数,则返回一个上下文管理器(上下文管理器将捕获的异常对象存储在其异常属性中),
以便测试中的代码可以内联编写,而不是作为函数编写
eg:
class TestAssertError(unittest.TestCase):
def do_something(self):
with self.assertRaises(ZeroDivisionError,1/0) as cm:
print('error')
the_exception = cm.exception
self.assertEqual(the_exception.error_code, 3)
def test_args_must_match_init(self):
self.assertRaises(ZeroDivisionError,1/0 )
- assertRaiseRegex(exc,r,fun,args,kwds) 判断fun(args,kwds) raises exc and the message matches regex r
解析:与assertRaises()类似,但也测试正则表达式是否与引发的异常的字符串表示形式匹配。正则表达式可以是正则表达式对象,
也可以是包含适合re使用的正则表达式的字符串
eg:
self.assertRaisesRegex(ValueError, "invalid literal for.XYZ'$",
int, 'XYZ')
with self.assertRaisesRegex(ValueError, 'literal'):
int('XYZ')
- assertWarns(warn,fun,args,kwds) 判断fun(args,kwds) raises warn
解析:测试在使用任何位置或关键字参数(也传递给AssertWarnings())调用callable时是否触发警告。
如果触发警告,则测试通过;如果未触发警告,则测试失败。任何异常都是错误。要捕获一组警告中的任何一个,可以将包含警告类的元组作为警告传递
- assertWarns(warning, *, msg=None) 返回一个上下文管理器
解析:如果只给出了警告和可能的msg参数,则返回上下文管理器(上下文管理器将捕获的异常对象存储在其异常属性中),以便测试中的代码可以内联编写,而不是作为函数编写
eg:
with self.assertWarns(SomeWarning) as cm:
do_something()
self.assertIn('myfile.py', cm.filename)
self.assertEqual(320, cm.lineno)
- assertWarnsRegex(warn,r,fun,*args,**kwds) 判断fun(*args,**kwds) raises warn and the message matches regex r
- assertWarnsRegex(warning, regex, *, msg=None)
解析:与AssertWarnings()类似,但也测试正则表达式是否与触发的警告消息匹配。正则表达式可以是正则表达式对象,也可以是包含适合re使用的正则表达式的字符串
eg:
self.assertWarnsRegex(DeprecationWarning,
r'legacy_function\(\) is deprecated',
legacy_function, 'XYZ')
with self.assertWarnsRegex(RuntimeWarning, 'unsafe frobnicating'):
frobnicate('/etc/passwd')
- assertLogs(Logger,level) 日志记录器上记录了至少一条消息,如果with块内发出的至少一条消息与记录器和级别条件匹配,则测试通过,否则测试失败
解析:一种上下文管理器,用于测试记录器或其子项上是否至少记录了一条消息,且至少具有给定的级别;如果给定Logger,记录器应该是日志记录。
Logger对象或提供记录器名称的str。默认为根记录器,它将捕获未被非传播后代记录器阻止的所有消息;如果给定level,级别应该是数字日志级别或
其等效字符串(例如“ERROR”或logging.ERROR)。默认设置是日志记录。
上下文管理器返回的对象是一个记录助手,它跟踪匹配的日志消息。它有两个属性:records(日志记录列表)、output(具有匹配消息格式化输出的str对象列表)
eg:
with self.assertLogs('foo', level='INFO') as cm:
logging.getLogger('foo').info('first message')
logging.getLogger('foo.bar').error('second message')
self.assertEqual(cm.output, ['INFO:foo:first message',
'ERROR:foo.bar:second message'])
-
assertNoLogs(logger,level) 判断日志记录器上不会纪录最低级别的块日志(上下文管理器不会返回任何内容)
-
assertAlmostEqual(a,b) 判断round(a-b,7) == 0
解析:通过计算差值,四舍五入到给定的小数位数(默认为7),并与零进行比较,测试第一个和第二个是否近似相等。请注意,这些方法将值四舍五入到给定的小数位数 -
assertNotAlmostEqual(a,b) 判断round(a-b,7) != 0
-
assertGreater(a,b) 判断a > b
-
assertGreaterEqual(a,b) 判断a >= b
-
assertLess(a,b) 判断a < b
-
assertLessEqual(a,b) 判断a <= b
-
assertRegex(s,r) 判断r.search(s)
-
assertNotRegex(s,r) 判断 not r.search(s)
-
assertCountEqual(a,b) 判断a与b有相同的元素,验证两个序列中的每个元素是否具有相同的计数,如果没有,将生成一条错误消息,列出序列之间的差异
测试组
unittest.TestSuit(tests=()) 用于将测试聚合到应该一起运行的测试组中
- addTest(test) 添加一个测试用例或测试套件到suit中
- addTests(tests) 将TestCase和TestSuite实例的iterable中的所有测试添加到此测试套件,相当于迭代测试,为每个元素调用addTest()
- run(result) 运行与此套件关联的测试,将结果收集到作为结果传递的测试结果对象中。注意,与TestCase不同。TestSuite.run()要求传入结果对象
- debug() 运行与此套件关联的测试,但不收集结果。这允许将测试引发的异常传播到调用方,并可用于支持在调试器下运行测试
- countTestCases() 返回此测试对象表示的测试数,包括所有单个测试和子套件
iter()
加载和运行测试
-
unittest.TestLoader TestLoader类用于从类和模块创建测试套件。通常,不需要创建此类的实例;unittest模块提供了一个可以作为unittest共享的实例:defaultTestLoader 但是,使用子类或实例可以自定义某些可配置属性
-
loadTestsFromTestCase(testCaseClass) 返回TestCase派生的TestCase类中包含的所有测试用例的套件
-
loadTestsFromModule(module, pattern=None) 返回给定模块中包含的所有测试用例的套件。此方法在模块中搜索从TestCase派生的类,并为该类定义的每个测试方法创建该类的实例
适用于:当装置不同并且在子类中定义时 -
loadTestsFromName(name, module=None) 返回一组给定字符串说明符的所有测试用例
-
loadTestsFromNames(names, module=None) 与loadTestsFromName()类似,但采用名称序列而不是单个名称。返回值是一个测试套件,它支持为每个名称定义的所有测试
-
getTestCaseNames(testCaseClass) 返回在testCaseClass中找到的方法名的排序序列
-
discover(start_dir, pattern=‘test*.py’, top_level_dir=None) 通过从指定的开始目录递归到子目录来查找所有测试模块,并返回包含它们的TestSuite对象,所有测试模块必须可从项目顶层导入。如果开始目录不是顶级目录,则必须单独指定顶级目录,start_dir可以是虚线模块名,也可以是目录
-
TestLoader的以下属性可以通过子类化或实例赋值进行配置:
testMethodPrefix 给出将被解释为测试方法的方法名称的前缀。默认值为“test”,会影响getTestCaseNames()和所有loadTestsFrom*()方法
sortTestMethodsUsing 用于在getTestCaseNames()和所有loadTestsFrom*()方法中对方法名进行排序时比较方法名
suiteClass 从测试列表构造测试套件的可调用对象。结果对象上不需要任何方法。默认值是TestSuite类
testNamePatterns Unix shell样式通配符测试名称模式的列表,测试方法必须匹配这些模式才能包含在测试套件中 -
unittest.TestResult 用于编译有关哪些测试成功,哪些测试失败的信息(TestResult对象存储一组测试的结果,TestCase和TestSuite类确保正确记录结果)
errors 错误,包含2元组的TestCase实例和包含格式化回溯的字符串的列表。每个元组表示引发意外异常的测试
failures 失败,包含2元组的TestCase实例和包含格式化回溯的字符串的列表。每个元组表示一个测试,其中使用TestCase显式通知失败。断言*()方法
skipped 跳过,包含2元组的TestCase实例和字符串的列表,其中包含跳过测试的原因
expectedFailures 预期的失败,包含2元组的TestCase实例和包含格式化回溯的字符串的列表。每个元组表示测试用例的预期失败或错误
unexpectedSuccesses 意外的成功,包含标记为预期失败但成功的TestCase实例的列表
shouldStop 应该停止,当测试的执行应按stop()停止时,设置为True
testsRun 测试运行,到目前为止运行的测试总数
buffer 缓冲器,如果设置为true,则为sys。标准输出和系统。stderr将在调用的startTest()和stopTest()之间缓冲。
收集的输出将仅回显到实际系统上。标准输出和系统。如果测试失败或出现错误,则返回stderr。任何输出也会附加到故障/错误消息
failfast 失败,如果设置为true,将在第一次失败或错误时调用stop(),从而停止测试运行
tb_locals 如果设置为true,则局部变量将显示在回溯中
wasSuccessful() 如果到目前为止运行的所有测试都已通过,则返回True,否则返回False
stop() 通过将shouldStop属性设置为True,可以调用此方法来发出信号,表明正在运行的测试集应该中止,
例如,TextTestRunner类使用此功能在用户从键盘发出中断信号时停止测试框架。提供TestRunner实现的交互式工具可以以类似的方式使用它 -
startTest(test) 当测试用例测试即将运行时调用
-
stopTest(test) 在执行测试用例测试后调用,无论结果如何
-
startTestRun() 在执行任何测试之前调用一次
-
stopTestRun() 在执行所有测试后调用一次
-
addError(test, err) 当测试用例测试引发意外异常时调用。err是sys返回的形式的元组。exc_info():(类型、值、回溯),
默认实现将一个元组(test,formatted_err)附加到实例的errors属性,其中formatted_err是从err派生的格式化回溯 -
addFailure(test, err) 当测试用例测试发出失败信号时调用。err是sys返回的形式的元组。exc_info():(类型、值、回溯)
默认实现将一个元组(test,formatted_err)附加到实例的failures属性,其中formatted_err是从err派生的格式化回溯 -
addSuccess(test) 当测试用例测试成功时调用,默认实现不执行任何操作
-
addSkip(test, reason) 在跳过测试用例测试时调用。原因是测试给出的跳过的原因.默认实现将一个元组(test、reason)附加到实例的skipped属性
-
addExpectedFailure(test, err) 当测试用例测试失败或出现错误时调用,但标记为expectedFailure()decorator
默认实现将一个元组(test,formatted_err)附加到实例的expectedFailures属性,其中formatted_err是从err派生的格式化回溯 -
addUnexpectedSuccess(test) 当测试用例测试标记为expectedFailure()decorator但成功时调用,默认实现将测试附加到实例的unexpectedsuccessions属性
-
addSubTest(test, subtest, outcome) 当子测试完成时调用。test是与测试方法相对应的测试用例。subtest是描述子测试的自定义测试用例实例,如果结果为“无”,则子测试成功。否则,它将失败,出现一个异常,其中结果是sys返回的形式的元组。exc_info():(类型、值、回溯)
-
unittest.TextTestResult(stream, descriptions, verbosity) 使用的TestResult的具体实现
-
unittest.defaultTestLoader 要共享的TestLoader类的实例。如果不需要定制TestLoader,则可以使用该实例,而不是重复创建新实例
-
unittest.TextTestRunner(stream=None, descriptions=True, verbosity=1, failfast=False, buffer=False, resultclass=None, warnings=None, *, tb_locals=False)
将结果输出到流的基本测试运行程序实现。如果stream为None,则默认值为sys。stderr用作输出流。这个类有几个可配置的参数,
但本质上非常简单。运行测试套件的图形应用程序应该提供替代实现。这些实现应该接受**KWARG,因为当特性添加到unittest时,构建Runner的接口会发生变化 -
_makeResult() 此方法返回run()使用的TestResult实例
-
run(test) 此方法是TextTestRunner的主要公共接口。此方法采用TestSuite或TestCase实例。通过调用_makeResult()创建TestResult,然后运行测试并将结果打印到stdout
-
unittest.main(module=‘main’, defaultTest=None, argv=None, testRunner=None, testLoader=unittest.defaultTestLoader,
exit=True, verbosity=1, failfast=None, catchbreak=None, buffer=None, warnings=None)
从模块加载一组测试并运行它们的命令行程序;这主要是为了使测试模块易于执行
defaultTest 单个测试的名称,或要运行的测试名称的iterable(如果没有通过argv指定测试名称)。如果未指定,或者通过argv未提供None和测试名称,则运行模块中找到的所有测试
argv参数可以是传递给程序的选项列表,第一个元素是程序名。如果未指定或无,则sys的值
testRunner参数可以是测试运行程序类,也可以是已创建的实例。默认情况下,main调用sys
exit(),其退出代码指示测试运行的成功或失败
testLoader testLoader实例,默认为defaultTestLoader
verbosity 输出更详细的信息 -
load_tests(loader, standard_tests, pattern) 模块或包可以通过实现名为load_tests的函数,自定义在正常测试运行或测试发现期间如何从它们加载测试,返回一个suit
eg:
test_cases = (TestCase1, TestCase2, TestCase3)
def load_tests(loader, tests, pattern):
suite = TestSuite()
for test_class in test_cases:
tests = loader.loadTestsFromTestCase(test_class)
suite.addTests(tests)
return suite
def load_tests(loader, standard_tests, pattern):
# top level directory cached on loader instance
this_dir = os.path.dirname(__file__)
package_tests = loader.discover(start_dir=this_dir, pattern=pattern)
standard_tests.addTests(package_tests)
return standard_tests
setUpClass and tearDownClass
eg:
import unittest
class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls._connection = createExpensiveConnectionObject()
@classmethod
def tearDownClass(cls):
cls._connection.destroy()
注:如果在setUpClass期间引发异常,则不会运行该类中的测试,也不会运行tearDownClass。跳过的类将不会运行setUpClass或tearDownClass。如果异常是SkipTest异常,则该类将被报告为已跳过,而不是错误
setUpModule and tearDownModule
eg:
def setUpModule():
createConnection()
def tearDownModule():
closeConnection()
注意:如果在setUpModule中引发异常,则不会运行该模块中的任何测试,也不会运行tearDownModule。如果异常是SkipTest异常,则模块将被报告为已跳过,而不是错误
要添加即使在异常情况下也必须运行的清理代码,使用unittest.addModuleCleanup(function, /, *args, **kwargs),添加要在tearDownModule()之后
信号处理
- unittest.installHandler() 安装control-c处理程序。当信号响起时。接收到SIGINT(通常是响应用户按下control-c)时,所有注册的结果都会被调用stop()
- unittest.registerResult(result) 注册TestResult对象以进行control-c处理。注册一个结果会存储对它的弱引用,因此它不会阻止结果被垃圾收集。
如果未启用control-c处理,则注册TestResult对象不会产生任何副作用,因此测试框架可以无条件地注册它们创建的所有结果,而不管是否启用了处理 - unittest.removeResult(result) 删除已注册的结果。删除结果后,将不再对该结果对象调用stop(),以响应控件c
- unittest.removeHandler(function=None) 当在没有参数的情况下调用此函数时,如果已安装control-c处理程序,则会将其删除。此函数还可用作测试装饰器,以便在执行测试时临时删除处理程序
eg:
@unittest.removeHandler
def test_signal_handling(self):
...
不常用函数方法
-
addTypeEqualityFunc(typeobj, function)
解析:注册assertEqual()调用的特定于类型的方法,以检查两个类型完全相同的对象OBJ(不是子类)是否比较相等。
函数必须采用两个位置参数和第三个msg=None关键字参数,就像assertEqual()一样。它必须提升自我。failureException(msg),
当检测到前两个参数之间的不平等时——可能提供有用的信息,并在错误消息中详细解释不平等 -
assertMultiLineEqual(a, b) 测试第一个多行字符串是否等于第二个字符串。当不相等时,突出显示差异的两个字符串的差异将包含在错误消息中。将字符串与assertEqual()进行比较时,默认情况下使用此方法
-
assertSequenceEqual(a, b) 测试两个序列是否相等。如果提供了seq_类型,则第一个和第二个都必须是seq_类型的实例,否则将引发故障。如果序列不同,则构造一条错误消息,显示两者之间的差异。
此方法不是由assertEqual()直接调用的,而是用于实现AssertListQual()和assertTupleEqual() -
assertListEqual(a, b) 测试两个列表是否相等
-
assertTupleEqual(a, b) 测试两个元组是否相等
-
assertSetEqual(a, b) 测试两个集合是否相等
-
assertDictEqual(a, b) 测试两个字典是否相等
-
fail(msg=None) 无条件发出测试失败信号,错误消息为msg或None
-
failureException 该类属性给出测试方法引发的异常。如果一个测试框架需要使用一个专门的异常,可能是为了携带额外的信息,
那么它必须将这个异常子类化,以便与框架“公平竞争”。此属性的初始值为AssertionError -
longMessage 此class属性确定将自定义失败消息作为msg参数传递给失败的assertXYY调用时发生的情况。True是默认值。
在这种情况下,自定义消息将附加到标准故障消息的末尾。设置为False时,自定义消息将替换标准消息.
通过分配实例属性self,可以在单个测试方法中重写类设置。longMessage,在调用assert方法之前设置为True或False -
maxDiff 此属性通过在失败时报告差异的断言方法控制差异输出的最大长度。默认为80*8个字符。将maxDiff设置为None意味着没有最大长度的diff
-
countTestCases()
收集信息的methods
- countTestCases() 返回此测试对象表示的测试数。对于TestCase实例,该值始终为1。
- defaultTestResult() 返回应用于此测试用例类的测试结果类的实例(如果没有向run()方法提供其他结果实例)
id() 返回标识特定测试用例的字符串。这通常是测试方法的全名,包括模块和类名 - shortDescription() 返回测试的说明,如果未提供说明,则返回None。此方法的默认实现返回测试方法的docstring的第一行(如果可用),或者不返回
- addCleanup(function, /, *args, kwargs) 添加要在tearDown()之后调用的函数,以清理测试期间使用的资源。函数的调用顺序与它们的添加顺序(后进先出)相反
- doCleanups() 负责调用addCleanup()添加的所有清理函数。如果需要在tearDown()之前调用清理函数,则可以自己调用
- doCleanups()如果setUp()引发异常,则在tearDown()之后或setUp()之后无条件调用此方法
- addClassCleanup(function, /, *args, kwargs) 添加要在tearDownClass()之后调用的函数,以清理测试类期间使用的资源。函数的调用顺序与它们的添加顺序(后进先出)相反
如果setUpClass()失败,意味着没有调用tearDownClass(),则添加的所有清理函数仍将被调用 - doClassCleanups() 负责调用addClassCleanup()添加的所有清理函数。如果需要在tearDownClass()之前调用清理函数,则可以自己调用doClassCleanups()
- unittest.IsolatedAsyncioTestCase(methodName=‘runTest’) 这个类提供了一个类似于TestCase的API,还接受协同路由作为测试函数
- asyncSetUp() 准备测试夹具。这在setUp()之后调用。在调用测试方法之前立即调用它;除了AssertionError或SkipTest之外,
此方法引发的任何异常都将被视为错误,而不是测试失败。默认实现不执行任何操作 - asyncTearDown() 调用测试方法并记录结果后立即调用方法。这是在拆卸()之前调用的。即使测试方法引发异常,也会调用它,
因此子类中的实现可能需要特别小心检查内部状态。此方法引发的任何异常(AssertionError或SkipTest除外)都将被视为附加错误,
而不是测试失败(从而增加报告的错误总数)。无论测试方法的结果如何,仅当asyncSetUp()成功时才会调用此方法。默认实现不执行任何操作 - addAsyncCleanup(function, /, *args, **kwargs) 此方法接受可用作清理函数的协同程序
- run(result=None)
unittest.FunctionTestCase 允许测试运行程序驱动测试,但不提供测试代码可用于检查和报告错误的方法。这用于使用遗留测试代码创建测试用例,允许将其集成到基于unittest的测试框架中
四、unittest.mock 模拟对象库
简介:unittest.mock是一个用于测试的python库,允许使用模拟对象来替换受测系统的一些部分,并对这些部分如何被使用进行断言判断,从而使得单元测试将焦点只放在当前的单元功能。
提供的Mock类,能在整个测试套件中模拟大量的方法。创建后可以断言调用了哪些方法/属性及其参数。还可以以常规方式指定返回值并设置所需的属性
应用:1、开发人员某些接口还没有开发完毕
2、与第三方联调时,第三方拖了后腿,没准备好环境、数据都有可能。比如说我们测试的某个接口本身没有问题,但它依赖的某个接口有些问题,这就影响我们的正常测试任务进度
3、测试环境恶劣,模拟出无法访问的资源:比如说,你需要调用一个“墙”外的资源来方便自己调试,就可以自己Mock一个
4、开发只提供接口,数据自己搞
eg:
function.py
def add_and_multiply(x,y):
addition = x + y
multiple = multiply(x,y)
return addition,multiple
def multiply(x,y):
return x * y
func_test.py
class MyTestCase(unittest.TestCase):
# 正常用例
def test_add_and_multiply(self):
x = 3
y = 5
addition,multiple = function.add_and_multiply(x,y)
self.assertEqual(8,addition)
self.assertEqual(15,multiple)
# multiply函数不可用时
# @MOCK.patch.object(类名,"类中函数名")装饰/上下文管理区,模拟类或对象在模块测试,在测试过程中,指定的对象将被替换成为一个模拟,并在测试结束时还原
# @mock.patch("模块名.
函数名")
@patch("test_mock.function.multiply")
def test_add_and_multiply2(self,mock_multiply):
x = 3
y = 5
# 设定mock_multiply的返回值固定为15
mock_multiply.return_value = 15
addition,multiply = function.add_and_multiply(x,y)
# 校验mock_multiply方法的参数是否正确
mock_multiply.assert_called_once_with(3,5)
self.assertEqual(8,addition)
self.assertEqual(15,multiply)
# 执行真实函数
def test_add_and_multiply3(self):
x = 3
y = 5
# 若存在side_effect参数值,则该参数值覆盖return_value
function.multiply = mock.Mock(return_value=1,side_effect=function.multiply)
result = function.multiply(3,5)
print(result)
self.assertEqual(result,15)
if __name__ == "__main__":
unittest.main()