unittest原名为PyUnit,是由java的JUnit衍生而来。对于单元测试,需要设置预先条件,对比预期结果和实际结果。
整体结构:
unittest库提供了test cases, test suites, test fixtures,test runner:
- test case :通过继承TestCase类,我们可以创建一个test,或者一组tests,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。
- test suites : 测试套件,多个测试用例集合在一起,TestSuite也可以嵌套TestSuite。
test fixtures : setup + test case + teardown结构 - TestLoader:用来加载TestCase到TestSuite中,其中的方法从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,返回一个TestSuite实例。
test runner:执行测试用例,其中的run()会执行TestSuite/TestCase。 - TextTestResult:测试的结果会保存到TextTestResult实例中,包括运行用例数,成功数,失败数等。
1. 一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。
2. 而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
3. TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。
4. TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。 测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。
5. 而对一个测试用例环境的搭建和销毁,是一个fixture。
写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。这里加个说明,在Runner执行时,默认将执行结果输出到控制台,我们可以设置其输出到文件,在文件中查看结果(你可能听说过HTMLTestRunner,是的,通过它可以将结果输出到HTML中,生成漂亮的报告)。
unittest实例
mathfunc.py
|
|
test_mathfunc.py
接下来我们为这些方法写一个测试:
组织TestSuite
你写的用例可能会有先后关系,需要先执行方法A,再执行方法B),我们就要用到TestSuite了。我们添加到TestSuite中的case是会按照添加的顺序执行的。
test_suite.py
|
|
执行结果:
可以看到,执行情况跟我们预料的一样:执行了三个case,并且顺序是按照我们添加进suite的顺序执行的。
上面用了TestSuite的 addTests() 方法,并直接传入了TestCase列表,我们还可以:
注意,用TestLoader的方法是无法对case进行排序的,同时,suite中也可以套suite。
将结果输出到文件中
用例组织好了,但结果只能输出到控制台,这样没有办法查看之前的执行记录,我们想将结果输出到文件。很简单,看示例:
修改test_suite.py:
执行此文件,可以看到,在同目录下生成了UnittestTextReport.txt,所有的执行报告均输出到了此文件中,这下我们便有了txt格式的测试报告了。
test fixture之setUp() tearDown()
上面整个测试基本跑了下来,但可能会遇到点特殊的情况:如果我的测试需要在每次执行之前准备环境,或者在每次执行完之后需要进行一些清理怎么办?比如执行前需要连接数据库,执行完成之后需要还原数据、断开连接。总不能每个测试方法中都添加准备环境、清理环境的代码吧。
这就要涉及到我们之前说过的test fixture了,修改test_mathfunc.py:
我们添加了 setUp() 和 tearDown() 两个方法(其实是重写了TestCase的这两个方法),这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境,已备之后的测试。
我们再执行一次:
可以看到setUp和tearDown在每次执行case前后都执行了一次。
如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用 setUpClass() 与 tearDownClass():
执行结果如下:
可以看到setUpClass以及tearDownClass均只执行了一次。
跳过某个case
如果我们临时想要跳过某个case不执行怎么办?unittest也提供了几种方法:
skip装饰器
执行:
|
|
可以看到总的test数量还是4个,但divide()方法被skip了。
skip装饰器一共有三个 unittest.skip(reason)、unittest.skipIf(condition, reason)、unittest.skipUnless(condition, reason),skip无条件跳过,skipIf当condition为True时跳过,skipUnless当condition为False时跳过。
HTMLTestRunner输出漂亮报告
HTMLTestRunner不支持python 3,HtmlTestRunner是将单元测试的结果保存为html格式的测试报告的工具,这个工具的功能和HTMLTestRunner很像,而且名称也很像。但是它支持python3,而且生成的报告的样式更加美观。
访问这个url:
https://pypi.python.org/pypi/html-testRunner/1.0.3
然后下载html_testRunner-1.0.3-py2.py3-none-any.whl文件,接下来通过下面命令来安装:
修改我们的 test_suite.py:
并且输出了HTML测试报告,HTMLReport.html,如图:
测试代码覆盖率 Coverage.py
Coverage.py 是一个用来测试代码覆盖率的 Python 第三方库。它起初是由 Ned Batchelder 创建。在编程界,术语“覆盖”通常是用来描述测试的有效性,以及测试的实际覆盖率。coverage.py 库支持 Python 2.6 或者更高的版本,还兼容 Python 3 的最新版以及 PyPy 。
一切准备就绪,让我们使用测试文件来运行 coverage.py。打开终端并且进入我们刚才写的那两个文件所在的目录。然后通过以下方式执行 coverage.py:
注意,我们需要调用 run 才能让 coverage.py 运行指定的模块。如果模块接收参数,可以像正常运行这个的模块一样带上参数。当执行以上指令后,你会看到测试模块的输出,就像正常运行该模块一样。在当前目录下,你还会发现一个名字为 .coverage 的文件(注意开头的点号)。要想获得文件中的信息,需要执行以下指令:
|
|
执行这条指令将会在终端打印以下信息:
-m 选项告诉 coverage.py 你想在输出信息中显示 Missing 列。如果省略 -m 选项,就只能看到前四列信息。上面的输出表明,coverage 在执行完测试代码之后,判断我写的单体测试程序对 mymath 模块的覆盖率只有 67% 。 “Missing” 列表明哪些行代码没有被覆盖。如果你看过 coverage.py 指出的那些行代码,很快就会发现测试程序没有运行测试 subtract, multiply 和 divide 函数。
在尝试添加更多的覆盖率测试代码之前,先来学习一下怎么通过 coverage.py 来生成 HTML报告。只需要执行以下命令即可:
|
|
以上指令将会生成一个叫 htmlcov 的目录,其中包括各种各样的文件。进入这个目录,并通过浏览器打开 index.html 文件。在我的电脑上,浏览器加载了这样的页面:
实际上,你可以通过点击 Module 列中列出的文件名来打开一个新的页面,页面中将会明显标识出代码中没有被单体覆盖的部分。
总结一下
unittest是Python自带的单元测试框架,我们可以用其来作为我们自动化测试框架的用例组织执行框架。
unittest的流程:写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。
一个class继承unittest.TestCase即是一个TestCase,其中以 test 开头的方法在load时被加载为一个真正的TestCase。
verbosity参数可以控制执行结果的输出,0 是简单报告、1 是一般报告、2 是详细报告。
可以通过addTest和addTests向suite中添加case或suite,可以用TestLoader的loadTestsFrom__()方法。
用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例执行前布置环境,以及在用例执行后清理环境
我们可以通过skip,skipIf,skipUnless装饰器跳过某个case,或者用TestCase.skipTest方法。
参数中加stream,可以将报告输出到文件:可以用TextTestRunner输出txt报告,以及可以用HTMLTestRunner输出html报告。
Coverage.py 可以检测单体测试代码并且发现单体测试覆盖中的漏洞。如果不确定你的单体测试程序是否达标,那么使用这个库包将会帮助你找到那些存在的漏洞。即便如此,你仍然需要认真负责地编写高质量的测试程序。如果没有写出有效的测试,而且测试还通过了,那么 coverage.py 也无法帮到你。