单元测试四: 一个unittest单元测试框架示例
testCase:测试用例,为测试某个功能(通常是函数或类),而编写的一组测试输入、执行条件以及预期结果的代码;
以便测试某个程序是否满足预期的需求。
unittest的4个重要概念:
1.Test FixTure:就是一个测试环境的搭建setUp(self)和销毁tearDown(self),这就是一个fixture。
setUp()--比如,某个文件或目录必须存在、数据库需要初始化好、网络服务要准备好、访问的URL需要登录授权完毕等等。tearDown()---清理和还原的功效,这样不至于各个测试执行的时候有环境污染造成各种诡异情况。比如:删除测试生成的文件或目录、销毁测试用的数据库等等。每次调用测试后,都会执行这个方法,即使调用的测试错误(Error)也会调用
2.Test Case:测试用例,继承unittest.TestCase的类。
3.Test Suite:测试套件,包含了多个测试用例或测试套件的集合
4.test runner:跑测试的家伙。
测试错误(异常),可以简单理解成测试代码执行时候报错了,比如:测试代码中print a,而a没有进行变量声明。
测试失败(非异常,但没有得到预期的结果),可以简单理解成测试代码执行正常,但没有得到预期的测试结果,比如:测试代码中调用功能代码add(1, 2),但返回结果不是3。
测试忽略(Ignore):skip装饰器
单元测试的加载,两种方式:unitest.main()和testsuite
一、unittest.main()示例:通过unittest.main()来启动单元测试的测试模块
import unittest class BaiduTest(unittest.TestCase): def __init__(self, *args, **kwargs): super(BaiduTest, self).__init__(*args, **kwargs) # unittest.TestCase.__init__(*args, **kwargs) def setUp(self) -> None: print("setup=================") def test_case1(self): print("这是case1") self.assertEqual(1, 1, "1!=1") # print(aaa) def test_case2(self): # print(a) self.assertEqual(1, 2, "1!=2") print("1323") print(abc) def tearDown(self) -> None: print("删除生成的文件,销毁次单元测试生成的一切环境") if __name__ == "__main__": unittest.main()
注意:
- 每个case,都会分别启用一次setUp和tearDown。
- 当抛出异常的时侯,无论是出现测试错误,还是测试失败,此case后面的代码不会继续执行。
- 以上结论不论是使用unitest.main()还是testsuite都适用
测试结果:
setup================= 这是case1 删除生成的文件,销毁次单元测试生成的一切环境 setup================= 删除生成的文件,销毁次单元测试生成的一切环境 .F ====================================================================== FAIL: test_case2 (__main__.BaiduTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:/Users/Administrator/PycharmProjects/untitled/1.py", line 14, in test_case2 self.assertEqual(1, 2, "1!=2") AssertionError: 1 != 2 : 1!=2 ---------------------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1)
二.testsuite加载集合:通过添加到testsuite集合中,再加载所有的被测试对象;而testsuite里存放的就是单元测试的用例。
import unittest class MyTestCase1(unittest.TestCase): def setUp(self): print("启动Case1") def test_case1(self): print("这是case1测试用例1") # 断言 self.assertEqual(1, 1, '不相等') def test_case2(self): print("这是case1测试用例2") # 断言 self.assertEqual(1, 1, '不相等') def tearDown(self): print("结束Case1") class MyTestCase2(unittest.TestCase): def setUp(self): print("启动Case2") def test_case1(self): print("这是case2测试用例1") # 断言 self.assertEqual(1, 1, '不相等') def test_case2(self): print("这是case2测试用例2") # 断言 self.assertEqual(1, 1, '不相等') def tearDown(self): print("结束Case2") if __name__ == '__main__': # 1.测试用例集合1 suite1 = unittest.TestLoader().loadTestsFromTestCase(MyTestCase1) # 1.测试用例集合2 suite2 = unittest.TestLoader().loadTestsFromTestCase(MyTestCase2) # 2.加载所有的被测试对象:将所有的测试用例集合,加在一起 suite = unittest.TestSuite([suite1, suite2]) # 3.运行testsuite测试 unittest.TextTestRunner(verbosity=2).run(suite)
测试结果:
C:\Users\Administrator\AppData\Local\Programs\Python\Python37\python.exe C:/Users/Administrator/PycharmProjects/untitled/1.py test_case1 (__main__.MyTestCase1) ... ok test_case2 (__main__.MyTestCase1) ... ok test_case1 (__main__.MyTestCase2) ... ok test_case2 (__main__.MyTestCase2) ... ok ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK 启动Case1 这是case1测试用例1 结束Case1 启动Case1 这是case1测试用例2 结束Case1 启动Case2 这是case2测试用例1 结束Case2 启动Case2 这是case2测试用例2 结束Case2
补充:
1.生成单个suite套件的方式:
既可以用loader---unittest.TestLoader(),然后loader.loadTestsFromTestCase(case类)得到;以上示例所使用的方法。
也可以用unittest.makeSuite(Case类);以下示例所使用的方法。
也可以用在只添某一个测试项时,跳过loader或makeSuite,直接实例化case类,Case("某测试项");其方法总结,见后章节。
2. 组装多个suite套件的方式:
可以在初始化unittest.TestSuite([suit1, suit2, case1, case2])类的时侯,在初始化方法中,传入suite或case实例对象;
也可以,先初始化一个空的suite集合,在后面它的使用addTest或addTests方法添加。
上面示例中,生成suite的方法,是在初始化TestSuite类的时侯,添加suite或case:suite = unittest.TestSuite([suite1, suite2])
也可以,在初始化的时侯不添加测试用例case或测试套件suite,而在后面调addTest或addTests来添加。
# 方式一:实始化的时侯,添加测试用例或测试套件 suite = unittest.TestSuite([suite1, suite2]) # 方式二:调用addTest或addTests方法添加 suite = unittest.TestSuite() suite.addTests(suite1) suite.addTests(suite2)
3.以上示例中的TestLoader()方法:
上面示例中,生成suite,使用的是TestLoader方式将测试用例添加到测试套件中:
suite1 = unittest.TestLoader().loadTestsFromTestCase(MyTestCase1)
TestLoader是用来加载TestCase到TestSuite中的
TextTestRunner()是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法
测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。
4.以上的整个流程就是首先要写好TestCase,然后由TestLoader加载TestCase整个类的case到生成单个suite套装,再使用TestSuite将所有单个的suite再组装成suite集合,然后由TextTestRunner来运行suite集合,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。
三、makeSuite(case类)方法:
case类("某测试项")方法:
把某测试用例的所有测试方法都添加到suite,makeSuite方法:suite.addTest(unittest.makeSuite(MyTestCase1))
只添加某测试用例的某个测试方法添加到suite:suite.addTest(MyTestCase1('test_case1'))
#-*- encoding: UTF-8 -*- import unittest class ExampleTestCase(unittest.TestCase): def test_do_somthing(self): self.assertEqual(1, 1) def test_do_somthing_else(self): self.assertEqual(1, 1) class AnoterExampleTestCase(unittest.TestCase): def test_do_somthing(self): self.assertEqual(1, 1) def test_do_somthing_else(self): self.assertEqual(1, 1) def suite_use_make_suite(): """想把TestCase下的所有测试加到TestSuite的时候可以这样用 """ suite = unittest.TestSuite() suite.addTest(unittest.makeSuite(ExampleTestCase)) return suite def suite_add_one_test(): """想把TestCase下的某个测试加到TestSuite的时候可以这样用 """ suite = unittest.TestSuite() suite.addTest(ExampleTestCase('test_do_somthing')) return suite def suite_use_test_loader(): """想用TestLoader方式把测试加到TestSuite的死后可以这样用 """ test_cases = (ExampleTestCase, AnoterExampleTestCase) suite = unittest.TestSuite() for test_case in test_cases: tests = unittest.defaultTestLoader.loadTestsFromTestCase(test_case) suite.addTests(tests) return suite if __name__ == '__main__': unittest.main(defaultTest='suite_use_test_loader')
测试结果:
.... ---------------------------------------------------------------------- Ran 4 tests in 0.000s OK
四、跳过某个case:skip装饰器
skip装饰器一共有三个 :
unittest.skip(reason)
unittest.skipIf(condition, reason)
unittest.skipUnless(condition, reason)
skip无条件跳过,skipIf当condition为True时跳过,skipUnless当condition为False时跳过。
五、用HTMLTestRunner输出漂亮的HTML报告
# -*- coding: utf-8 -*- import unittest from test_mathfunc import TestMathFunc from HTMLTestRunner import HTMLTestRunner if __name__ == '__main__': suite = unittest.TestSuite() suite.addTests(unittest.TestLoader().loadTestsFromTestCase(TestMathFunc)) with open('HTMLReport.html', 'w') as f: runner = HTMLTestRunner(stream=f, title='MathFunc Test Report', description='generated by HTMLTestRunner.', verbosity=2 ) runner.run(suite)
verbosity参数可以控制执行结果的输出,0 是简单报告、1 是一般报告、2 是详细报告
参数中加stream,可以将报告输出到文件:可以用TextTestRunner输出txt报告,以及可以用HTMLTestRunner输出html报告。
六、doctest:直接把测试写在文档注释。其中一个优点是,看到注释就知道这个模块、函数、类是怎么个用法了;而其中一个缺点是,测试代码的组织上很难模块化。
def show_me_the_money():
"""
>>> print show_me_the_money()
$
"""
return '$'
if __name__ == '__main__':
import doctest
doctest.testmod()
执行python test_doctest.py -v,即可。
- 还有各种成熟的各种开源开发库,比如:Python的Web开发框架Django,它里面就提供了适合Web开发场景的单元测试各种类库。
- 还有需要模拟各种情况的类库,比如:网络请求、数据库存储、读写文件等等,Python中就提供了不少好的模拟的库,如 Mock
posted on 2018-08-24 22:01 myworldworld 阅读(498) 评论(0) 收藏 举报