python之Unittest单元测试框架

简介: 参考来源:Mushishi_xu博主和huilan_same同行的分享前言unittest是一个python版本的junit,junit是java中的单元测试框架,对java的单元测试,有一句话很贴切:Keep the bar green,相信使用eclipse写过java单元测试的都心领神会。

参考来源:Mushishi_xu博主huilan_same同行的分享

前言

unittest是一个python版本的junit,junit是java中的单元测试框架,对java的单元测试,有一句话很贴切:Keep the bar green,相信使用eclipse写过java单元测试的都心领神会。unittest实现了很多junit中的概念,作为标准python中的一个模块,是其它框架和工具的基础,参考资料是它的官方文档:http://docs.python.org/2.7/library/unittest.html和源代码,比如我们非常熟悉的test case, test suite等,总之,原理都是相通的,只是用不同的语言表达出来。

一、unittest工作原理

unittest中最核心的四个概念是:test case, test suite, test runner, test fixture。

img_940bade240d7464266a84c7ceda8c676.png
工作原理

一个TestCase的实例就是一个测试用例。什么是测试用例呢?就是一个完整的测试流程,包括测试前准备环境的搭建(setUp),执行测试代码(run),以及测试后环境的还原(tearDown)。元测试(unit test)的本质也就在这里,一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。

而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。

TestLoader是用来加载TestCase到TestSuite中的,其中有几个loadTestsFrom__()方法,就是从各个地方寻找TestCase,创建它们的实例,然后add到TestSuite中,再返回一个TestSuite实例。

TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法。测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

而对一个测试用例环境的搭建和销毁,是一个fixture。

一个class继承了unittest.TestCase,便是一个测试用例,但如果其中有多个以 test 开头的方法,那么每有一个这样的方法,在load的时候便会生成一个TestCase实例,如:一个class中有四个test_xxx方法,最后在load到suite中时也有四个测试用例。

到这里整个流程就清楚了:

写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。这里加个说明,在Runner执行时,默认将执行结果输出到控制台,我们可以设置其输出到文件,在文件中查看结果(通过HTMLTestRunner将结果输出到HTML中,生成漂亮的报告,它跟TextTestRunner是一样的)。

二、unittest实例-test case

1.准备测试方法

mathfunc.py

#coding:utf-8

import math

def add(a,b):

    return a + b

def minus(a,b):

    return a - b

def multi(a,b):

    return a * b

def divide(a,b):

    return a / b

2.为测试方法写测试用例

run_mathfunc.py

#coding:utf-8

import unittest

from python_ceshikuangjia.mathfuncimport *

class TestMathFunc(unittest.TestCase):

    def test_add(self):

        self.assertEqual(5,add(3.2))

        self.assertNotEqual(3,add(2,2))

    def test_minus(self):

        self.assertEqual(2,minus(4,2))

    def test_multi(self):

        self.assertEqual(6,multi(2,3))

    def test_divide(self):

        self.assertEqual(2,divide(6,3))

        self.assertEqual(2.5,divide(5,2))

if __name__ =='__main__':

    unittest.main()

3.查看运行结果

img_f18840b4e4d6a29937b56e1ed13b4bea.png
success


这就是一个简单的测试,有几点需要说明的:

>在第一行给出了每一个用例执行的结果的标识,成功是 .,失败是 F,出错是 E,跳过是 S。从上面也可以看出,测试的执行跟方法的顺序没有关系,test_divide写在了第4个,但是却是第2个执行的。

>每个测试方法均以 test 开头,否则是不被unittest识别的。

>在unittest.main()中加 verbosity 参数可以控制输出的错误报告的详细程度,默认是 1,如果设为 0,则不输出每一用例的执行结果,即没有上面的结果中的第1行;如果设为 2,则输出详细的执行结果,如下:

img_09fdbd2f089fd51157a834f00ca0e327.png
详细结果输出

三、组织TestSuite

上面的代码示例了如何编写一个简单的测试,但有两个问题,我们怎么控制用例执行的顺序呢?(这里的示例中的几个测试方法并没有一定关系,但之后你写的用例可能会有先后关系,需要先执行方法A,再执行方法B),我们就要用到TestSuite了。我们添加到TestSuite中的case是会按照添加的顺序执行的。

问题二是我们现在只有一个测试文件,我们直接执行该文件即可,但如果有多个测试文件,怎么进行组织,总不能一个个文件执行吧,答案也在TestSuite中。

请看run_suite.py

#coding:utf-8

import unittest

from python_ceshikuangjia.run_mathfuncimport TestMathFunc

if __name__ =='__main__':

    suite = unittest.TestSuite()

    tests = [TestMathFunc("test_add"),TestMathFunc("test_minus"),TestMathFunc("test_divide")]

    suite.addTests(tests)

    runner = unittest.TextTestRunner(verbosity=2)

    runner.run(suite)

运行结果:

img_8a88b7437e9fb742ce29f8aadc0b231a.png
运行结果

可以看到,执行情况跟我们预料的一样:执行了三个case,并且顺序是按照我们添加进suite的顺序执行的。那么,如何将结果输出到文件呢,请看下面操作方法,修改run_suite.py代码,如下:

#coding:utf-8

import unittest

from python_ceshikuangjia.run_mathfuncimport TestMathFunc

if __name__ =='__main__':

    suite = unittest.TestSuite()

    tests = [TestMathFunc("test_add"),TestMathFunc("test_minus"),TestMathFunc("test_divide")]

    suite.addTests(tests)

    with open('D:/work/python_ceshikuangjia/run_suite_log.txt','a')as f:

        runner = unittest.TextTestRunner(stream=f,verbosity=2)

        runner.run(suite)

img_c3d2bb7411541363f0fba82d27c487f3.png
运行结果

四、test fixture之setUp() tearDown()

1.假如我的测试需要在每次执行之前准备环境,或者在每次执行完之后需要进行一些清理怎么办?比如执行前需要连接数据库,执行完成之后需要还原数据、断开连接。总不能每个测试方法中都添加准备环境、清理环境的代码吧,这时,就轮到test fixture之setUp() tearDown()大展身手的时候了,请看如下代码:在run_mathfunc.py下的class TestMathFunc类中添加如下代码

class TestMathFunc(unittest.TestCase):

    def setUp(self):

        print("do something before test.Prepare environment.")

    def tearDown(self):

        print("do something after test.Clean up.")

 setUp() 和 tearDown() 两个方法(其实是重写了TestCase的这两个方法),这两个方法在每个测试方法执行前以及执行后执行一次,setUp用来为测试准备环境,tearDown用来清理环境,已备之后的测试。

运行结果如下:

img_c219962cc15c7a8609e0e6a88e4582bd.png
运行结果

可以看到setUp和tearDown在每次执行case前后都执行了一次。

2.如果想要在所有case执行之前准备一次环境,并在所有case执行结束之后再清理环境,我们可以用 setUpClass() 与 tearDownClass():请看如下代码:在run_mathfunc.py下的class TestMathFunc类中添加如下代码

class TestMathFunc(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

        print("This setUpClass() method only called once.")

    @classmethod

    def tearDownClass(cls):

        print("This tearDownClass() method only called once too.")

运行结果:

img_6ad0f30b147872cd9f64b1e107799dd8.png
运行结果

可以看到setUpClass以及tearDownClass均只执行了一次。

3.运行测试用例时不想全部运行,或者说想跳过某一个用例,那么这时skip装饰器就起作用了。

skip装饰器一共有三个 unittest.skip(reason)、unittest.skipIf(condition, reason)、unittest.skipUnless(condition, reason),skip无条件跳过,skipIf当condition为True时跳过,skipUnless当condition为False时跳过。以下分两种情况进行解析。

>skip装饰器

class TestMathFunc(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

        print("This setUpClass() method only called once.")

    @classmethod

    def tearDownClass(cls):

        print("This tearDownClass() method only called once too.")

    @unittest.skip(u"我不想运行此用例!!.")

    def test_add(self):

        self.assertEqual(5,add(3,2))

        self.assertNotEqual(3,add(2,2))

运行结果:

img_80c975004466791dd6c730708142fbb0.png
运行结果

>TestCase.skipTest()方法

class TestMathFunc(unittest.TestCase):

    @classmethod

    def setUpClass(cls):

        print("This setUpClass() method only called once.")

    @classmethod

    def tearDownClass(cls):

        print("This tearDownClass() method only called once too.")

#    @unittest.skip(u"我不想运行此用例!!.")

    def test_add(self):

        self.assertEqual(5,add(3,2))

        self.assertNotEqual(3,add(2,2))

    def test_minus(self):

        self.skipTest(u"我不想运行此用例!!")

        self.assertEqual(2,minus(4,2))

运行结果:

img_4c7b66027ae493c0e1afe9514b27fb15.png
运行结果

通过以上两种不同方式,可以看到总的test数量还是3个,但add()和minus()方法都被skip了。

五、用HTMLTestRunner输出HTML报告

HTMLTestRunner是一个第三方的unittest HTML报告库,首先我们下载HTMLTestRunner.py,并放到当前目录下,或者你的’python’安装目录下,就可以导入运行了。

下载地址:HTMLTestRunner模板  (下载的模板只支持python2.x,要想在python3.x中使用可以看下这个:HTMLTestRunner修改成Python3版本

修改我们的 run_suite.py:

#coding:utf-8

import unittest

import HTMLTestRunner

from python_ceshikuangjia.run_mathfuncimport TestMathFunc

if __name__ =='__main__':

    suite = unittest.TestSuite()

    tests = [TestMathFunc("test_add"),TestMathFunc("test_minus"),TestMathFunc("test_divide"),TestMathFunc('test_multi')]

    suite.addTests(tests)

# with open('D:/work/python_ceshikuangjia/run_suite_log.txt','a') as f:

#    runner = unittest.TextTestRunner(stream=f,verbosity=2)

#    runner.run(suite)

#输出HTML格式报告

    with open('D:/work/python_ceshikuangjia/HTMLReport.html','wb')as f:

        runner = HTMLTestRunner.HTMLTestRunner(stream=f,

                                title=u'软件测试报告 Test Report',

                                description=u'用例执行情况',

                                verbosity =2)

        runner.run(suite)

运行结果:

img_7db539051f129d53258e193bb89cc756.png
运行结果1
img_34d09d14ff36f9ba83db85fad5b9224b.png
运行结果2

这下漂亮的HTML报告也有了。其实你能发现,HTMLTestRunner的执行方法跟TextTestRunner很相似,你可以跟我上面的示例对比一下,就是把类图中的runner换成了HTMLTestRunner,并将TestResult用HTML的形式展现出来,如果你研究够深,可以写自己的runner,生成更复杂更漂亮的报告。

单元测试小结:

1.unittest是Python自带的单元测试框架,我们可以用其来作为我们自动化测试框架的用例组织执行框架。

2.unittest的流程:写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,我们通过命令行或者unittest.main()执行时,main会调用TextTestRunner中的run来执行,或者我们可以直接通过TextTestRunner来执行用例。

3.项目命名不可用小写‘test’开头(大写无影响),否则会出错,一个class继承unittest.TestCase即是一个TestCase,其中以 test 开头的方法在load时被加载为一个真正的TestCase。

4.verbosity参数可以控制执行结果的输出,0 是简单报告、1 是一般报告、2 是详细报告。

5.可以用 setUp()、tearDown()、setUpClass()以及 tearDownClass()可以在用例执行前布置环境,以及在用例执行后清理环境

6.我们可以通过skip,skipIf,skipUnless装饰器跳过某个case,或者用TestCase.skipTest方法。

7.参数中加stream,可以将报告输出到文件:可以用TextTestRunner输出txt报告,以及可以用HTMLTestRunner输出html报告,或者自己研究生成更复杂更漂亮的报告。

目录
相关文章
|
1月前
|
安全 测试技术 网络安全
如何在Python Web开发中进行安全测试?
如何在Python Web开发中进行安全测试?
|
1月前
|
安全 关系型数据库 测试技术
学习Python Web开发的安全测试需要具备哪些知识?
学习Python Web开发的安全测试需要具备哪些知识?
34 4
|
3天前
|
IDE 测试技术 开发工具
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
在Python开发中,调试是提升效率的关键技能。本文总结了10个实用的调试方法,涵盖内置调试器pdb、breakpoint()函数、断言机制、logging模块、列表推导式优化、IPython调试、警告机制、IDE调试工具、inspect模块和单元测试框架的应用。通过这些技巧,开发者可以更高效地定位和解决问题,提高代码质量。
43 8
10个必备Python调试技巧:从pdb到单元测试的开发效率提升指南
|
9天前
|
JSON 数据可视化 测试技术
python+requests接口自动化框架的实现
通过以上步骤,我们构建了一个基本的Python+Requests接口自动化测试框架。这个框架具有良好的扩展性,可以根据实际需求进行功能扩展和优化。它不仅能提高测试效率,还能保证接口的稳定性和可靠性,为软件质量提供有力保障。
33 7
|
7天前
|
分布式计算 大数据 数据处理
技术评测:MaxCompute MaxFrame——阿里云自研分布式计算框架的Python编程接口
随着大数据和人工智能技术的发展,数据处理的需求日益增长。阿里云推出的MaxCompute MaxFrame(简称“MaxFrame”)是一个专为Python开发者设计的分布式计算框架,它不仅支持Python编程接口,还能直接利用MaxCompute的云原生大数据计算资源和服务。本文将通过一系列最佳实践测评,探讨MaxFrame在分布式Pandas处理以及大语言模型数据处理场景中的表现,并分析其在实际工作中的应用潜力。
34 2
|
22天前
|
敏捷开发 测试技术 持续交付
自动化测试之美:从零开始搭建你的Python测试框架
在软件开发的马拉松赛道上,自动化测试是那个能让你保持节奏、避免跌宕起伏的神奇小助手。本文将带你走进自动化测试的世界,用Python这把钥匙,解锁高效、可靠的测试框架之门。你将学会如何步步为营,构建属于自己的测试庇护所,让代码质量成为晨跑时清新的空气,而不是雾霾中的忧虑。让我们一起摆脱手动测试的繁琐枷锁,拥抱自动化带来的自由吧!
|
1月前
|
监控 安全 测试技术
如何在实际项目中应用Python Web开发的安全测试知识?
如何在实际项目中应用Python Web开发的安全测试知识?
32 4
|
1月前
|
缓存 API 数据库
Python哪个框架合适开发速卖通商品详情api?
在跨境电商平台速卖通的商品详情数据获取与整合中,Python 语言及其多种框架(如 Flask、Django、Tornado 和 FastAPI)提供了高效解决方案。Flask 简洁灵活,适合快速开发;Django 功能全面,适用于大型项目;Tornado 性能卓越,擅长处理高并发;FastAPI 结合类型提示和异步编程,开发体验优秀。选择合适的框架需综合考虑项目规模、性能要求和团队技术栈。
27 2
|
1月前
|
测试技术 开发者 UED
探索软件测试的深度:从单元测试到自动化测试
【10月更文挑战第30天】在软件开发的世界中,测试是确保产品质量和用户满意度的关键步骤。本文将深入探讨软件测试的不同层次,从基本的单元测试到复杂的自动化测试,揭示它们如何共同构建一个坚实的质量保证体系。我们将通过实际代码示例,展示如何在开发过程中实施有效的测试策略,以确保软件的稳定性和可靠性。无论你是新手还是经验丰富的开发者,这篇文章都将为你提供宝贵的见解和实用技巧。
|
4月前
|
JSON Dubbo 测试技术
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决
61 2
单元测试问题之增加JCode5插件生成的测试代码的可信度如何解决