龙空技术网

python单元测试之unittest

老牛实验室 85

前言:

此刻我们对“python程序测试”都比较注重,各位老铁们都需要分析一些“python程序测试”的相关内容。那么小编在网摘上搜集了一些关于“python程序测试””的相关知识,希望大家能喜欢,看官们一起来了解一下吧!

一、unittest 框架

unittest架构

unittest是python自带的测试框架,是应该掌握的python基础技能。主要包括以下几个类:

TestCase 测试用例

建立此类的子类,内部建立以test开头方法,每个方法实现一个测试。

FunctionTestCase用于将已经有的测试方法,添加入unittest框架方式运行。

IsolatedAsyncioTestCase 为python3.8新增,适应异步协程测试。

TestSuite 测试集

测试用例的管理容器,按照加入测试集合的顺序执行测试。

TextTestRunner 运行器

测试的运行器,会返回TestResult。

TestResult 测试结果

记录测试的结果。TextTestResult,为TestResult 的具体实现,保存文本格式的结果。

TestLoader 测试加载器

用于测试用例的加载。unittest.defaultTestLoader为默认的加载器。

做一个比方,把TestCase当成子弹,TestSuite则相当于弹夹,TestLoader是向弹夹装子弹的装置,TextTestRunner则是手枪,TestResult 则是射击结果。

二、运行测试文件方式

运行环境为python工具virtualenv建立的虚拟环境

(a)python zagtest2.py (zagtest2.py不只有TestCase定义,还有自定义运行器部分)

按照zagtest2.py中自定义的测试顺序运行,具体说明见后文。

(b)python -m unittest zagtest2.py

用unittest默认运行器,加载运行测试模块zagtest2.py (扩展名.py可以不要)(即使zagtest2.py中有自定义运行器也不生效)。

发现输出test_1在先,与上图不一致。这是unittest的默认运行顺序,按照测试方法名字符顺序。

(c)python -m unittest

unittest自动在当前目录下查找test开头.py文件(包括子目录,文件中定义有TestCase),用默认运行器运行其中的测试用例。

python -m unittest运行输出

运行目录中有两个以test开头文件

三、测试实例讲解

基本TestCase实例(zagtest1.py)

import unittestclass TestCase1(unittest.TestCase):        @classmethod    def setUpClass(self):           	  #此方法第一个运行。只运行一次,        #可以在此处实现如数据库建立,数据准备等操作        print("execute setUpClass")    @classmethod    def tearDownClass(self):    		#此方法最后运行,只运行一次。        #可以在此处实现如数据清理,收尾工作        print("execute tearDownClass")    def setUp(self):    	#下面每个test开头函数运行前都执行,      #可以在此处初始化测试对象        print("execute setUp")    def tearDown(self):    	#下面每个test开头函数运行后都执行,      #可以在此处清除测试对象        print("execute tearDown")    def test_2(self):        """默认测试顺序:函数名的字符顺序"""        print('execute test_2')    def test_1(self):    	 """每个测试函数,完成一个独立功能测试。       命名以test开头"""        print('execute test_1')if __name__ == '__main__':    unittest.main()   #以unittest默认方式运行测试    #默认参数:    #unittest.main(module="__main__",testLoader=unittest.defaultTestLoader)

可以尝试一下,按照a、b两种方式运行,运行结果应该是相同的。

指定顺序运行TestCase

以b方式运行测试文件zagtest1.py,可以看到测试顺序是先 输出了【execute test_1】,后输出【execute test_2】。

运行顺序与方法定义顺序不一样。这就是unittest默认的运行方式,根据test开头函数的字符顺序运行。下面的代码,通过定义TestSuite,自定义测试顺序。

.........(代码同上文)if __name__=="__main__":    suite=unittest.TestSuite()         #定义TestSuite     #向TestSuite中添加2个测试,格式为:      #TestCase类名("测试方法名字")    suite.addTest(TestCase1("test_2"))      suite.addTest(TestCase1("test_1"))    runner=unittest.TextTestRunner()    runner.run(suite)

以方式(a)运行,则先输出'execute test_2,再输出execute test_1,是按照定义的顺序。

以Loader加载测试,输出测试报告到HTML文件

推荐将测试组织为3种文件:测试目标、测试用例、测试运行文件。

测试目标(myOPs.py)

class MyOPs:    def __init__(self,x,y):        self.x=x        self.y=y    def zadd(self):        return self.x+self.y    def zmult(self):        return self.x*self.y

定义测试案例(testCaseMyOPs.py)

import unittestfrom myOPs import MyOPsclass  TestCaseMyOPs(unittest.TestCase):    @classmethod    def setUpClass(cls) -> None:        print("setUpClass")    @classmethod    def tearDownClass(cls) -> None:        print("tearDownClass")    def setUp(self) -> None:        self.ops = MyOPs(2, 3)        print("setUp")    def tearDown(self) -> None:        self.ops=None        print("tearDown")    def test_zadd(self):        print("test_zadd")        self.assertEqual(self.ops.zadd(),4)    def test_zmult(self):        print("test_zmult")        self.assertEqual(self.ops.zmult(),6)

最后,建立测试运行文件(runTestMyOPs.py)

实验中使用了HTMLReport第三方模块(),输出测试结果到html文件。以python runTestMyOPs.py 方式运行。

import unittestimport HTMLReportfrom  testCaseMyOPs import TestCaseMyOPsif __name__=="__main__":    #注意:使用HTMLReport,必须加此判断    suite=unittest.TestSuite()    loader=unittest.TestLoader()   #从TestCase类中,加载定义的测试(2个)    suite.addTests(loader.loadTestsFromTestCase(TestCaseMyOPs))     result_file="TestMyOPs_report.html"    runner=HTMLReport.TestRunner(result_file,title="测试报告",description="加法与乘法测试",                                 lang="cn")    runner.run(suite)

测试文件默认输出到report目录下,可以点击【失败】【通过】,查看报告细节。

四、pycharm中快速建立测试文件

class Car:    def __init__(self,type="BMX"):        self.type=type        self.speed=0    def speed_up(self,new_speed):        self.speed = new_speed    def stop(self):        self.speed = 0    def show_msg(self):        return self.type+":"+str(self.speed)

有一个待测试类Car,定义有3个可测试方法。在Car类名上,点击鼠标右键,如下图选择。

自动命名测试用例文件为test_Car.py,测试用例类为TestCar。

可以选择要测试的3个函数,生成Car类的测试用例文件。

pycharm默认的测试框架为unittest,也可选择其他测试框架。参考下图更改。

五、注意事项及默认规则

命名规则

以unittest命令行方式运行测试文件,默认把test*.py认为是TestCase文件;

TestCase定义文件中,默认把 test* 方法,当做测试函数(没有load_tests方法时);

[晕]千万别把文件名命名为unittest.py

测试标记

测试结果显示中会见到:

s 表示跳过一个测试

. 测试通过

F 测试未通过

E 表示测试中发生错误(比如除0,对象不存在等)

FunctionTestCase用于将未使用测试框架写的测试,纳入到unittest统一方式

"""随便写的测试代码"""def sayHello():    return "hello"def testSayHello():    assert sayHello()=="hello"  suite.addTest(FunctionTestCase(testSayHello)) #此测试可以添加到testsuite中
测试独立方法
import unittestdef showMsg(msg):    return "%s"%(msg)def do_divide(a,b):    return a/bdef showTrue(flag):    return flagclass TestSomFunc(unittest.TestCase):    def testrun(self):        print("testrun")        self.assertEqual("OK",showMsg("OK"))        self.assertNotEqual("OK",showMsg("NO"))        self.assertTrue(do_divide(1,2))        self.assertIs(showTrue(False),False)        self.assertIs(int(do_divide(1,2)),1)if __name__=="__main__":    suite=unittest.TestSuite()    suite.addTest(TestSomFunc("testrun"))    suite.addTest(FunctionTestCase(testSayHello)) #将FunctionTestCase加入    runner=unittest.TextTestRunner()    runner.run(suite)
TestLoader自动查找测试的规则

在目录中运行python -m unittest,实现原理也就是内部运行loader.discover()查找TestCase。

def discover(self, start_dir, pattern='test*.py', top_level_dir=None)start_dir:为搜索起始目录pattern:认为什么样的文件中存储有TestCasetop_level_dir:默认为None,此时start_dir,即为当前项目的顶级位置(不是子目录)。                       如start_dir为子目录,则此处需自行指定到项目的顶级位置。                       

discover会在当前目录(包括子目录),查找满足pattern的文件。

python中将一个文件称为一个module(模块)。将一个有__init__.py的目录称为一个package(包)。

如果模块或包中__init__.py文件,有load_tests方法,则由此方法返回测试TestSuite。否则,则查找模块中test开头的方法。

下面模块test_module.py中的load_tests方法实例,用TestLoader.loadTestsFromModule也是同理。

import unittestclass TestCaseA(unittest.TestCase):    def test_1(self):        pass    def test_2(self):        passclass TestCaseB(unittest.TestCase):    def test_m(self):        passclass TestCaseC(unittest.TestCase):    def test_k(self):        pass    def test_x(self):        passtest_cases=(TestCaseA,TestCaseB,TestCaseC)def load_tests(loader,tests,pattern):    suite=unittest.TestSuite()    for test_class in test_cases:        tests=loader.loadTestFromTestCase(test_class)        suite.addTests(tests)    return suite

下面将介绍包__init__.py中的load_tests定义:

import unittestimport osdef load_tests(loader,standard_tests,pattern):    """    standard_tests:  代表顶层的TestSuite    """    this_dir=os.path.dirname(__file__)    package_tests=loader.discover(start_dir=this_dir,pattern=pattern)  #查找当前包中的tests    standard_tests.addTests(package_tests)  #添加到顶层的TestSuite    return standard_tests
条件测试

unittest中,可以根据条件状况有选择的进行测试。下面代码名为 testCondition.py

import unittestimport sys__version__=1class  TestCaseSkips(unittest.TestCase):    @unittest.skip("无条件跳过")       def test_nothing(self):        print("test_nothing")    @unittest.skipUnless(sys.platform.startswith("win"),"只支持windows")    def test_win(self):        print("test_win")    @unittest.skipIf(__version__<3,"版本太低")    def test_version(self):        print("test_version")

python -m unittest testCondition.py

在win10下运行

一个s表示跳过一个测试 .表示测试通过(只test_win运行)。

TestCase类也可以添加跳过标记,如下例。

@unittest.skipUnless(sys.platform.startswith("win"),"只支持windows")class MySkippedTestCase(unittest.TestCase):    def test_not_run(self):        pass

python unittest的官方文档地址:

本文如果对你有所帮助,希望能点赞支持。今后,会持续推出技术积累、实验笔记,感兴趣的话,可以关注!

标签: #python程序测试 #pythonunittest安装