认识unittest框架,学习unittest框架的使用
一.unittest是什么?
Unittest是python单元测试框架,是受到 JUnit 的启发,与其他语言中的主流单元测试框架有着相似的风格。其支持测试自动化,配置共享和关机代码测试。支持将测试样例聚合到测试集中,并将测试与报告框架独立。它不仅适用于单元测试,还在自动化测试领域占有一席之地。借助它组织执行测试用例,使用它提供的丰富的断言方法进行测试结果的比对,并结合HTMLTestRunner生成测试报告完成整个自动化测试流程。
二.简单使用示例
创建被测类calc.py
class count:def __init__(self, a, b):self.a = aself.b = b#计算加法def add(self):return self.a + self.b#计算减法def Subtraction(self):return self.a - self.b
通过unittest单元测试框架编写单元测试用例 test.py
from calc import count import unittest class CountTest(unittest.TestCase):def setUp(self) -> None:print("in case setUP") def tearDown(self) -> None:print("in case tearDow") def test_add(self):c = count(4, 5)self.assertEqual(c.add(), 9) def test_subtraction(self):f = count(9, 8)self.assertEqual(f.Subtraction(), 1) if __name__ == '__main__':CountTest.main()
说明:
首先引入unittest模块,创建testcount类继承unitest的testcase类。
setUp():用于测试用例执行前的初始化工作,与tearDown()相呼应,用于执行后的善后工作。
test_add中调用count类并传入要计算的数,通过调用add()方法得到两数相加的返回值,这里不再使用繁琐的异常处理,而是调用unitest框架所提供的assertEqual()对add()的返回值进行断言判断两者是否相等()。assertEqual()方法是由testcase类继承而来的。
main():unittest提供了全局的main()方法,使用它可以方便的将一个单元测试模块变成可以直接运行的测试脚本。main()方法使用Testloader类来搜索所有包含在该模块中以“test”命名开头的测试方法。
name:作为模块的内置属性,简单地说就是.py文件的调用方式。.py文件有两种使用方式作为模块调用和直接使用,如果它等于“main*”就表示是直接使用
三.unittest模块说明
TestCase:一个TestCase的实例就是一个测试用例,是一个完整的测试流程,包括测试前准备环境的搭建(setUp),实现测试过程的代码(run),测试后环境的还原(tearDown).
Test Suite:把多个测试用例集合在一起来执行。可以通过addTest加载TestCase到Test Suite(测试套件)中,从而返回一个TestSuite实例。
Test Runner:测试的执行,通过TextTestRunner类提供的run()方法来执行Test Suite/TestCase。Test Runner可以使用图形界面,文本界面,或者返回一个特殊的值的方式来表示测试执行的结果。
Test Fixture:对一个测试用例环境的初始化和清除。通过覆盖TestCase的setUp()和tearDown()方法来实现。tearDown()为下一个测试用例提供一个干净的环境。
四.断言方法
unittest框架的TestCase类提供的断言方法用于测试结果的判断
1.判断第一个参数和第二个参数是否相等
-assertEqual(first,second,msg=None) # 如果不相等则测试失败,msg为可选参数,用于定义测试失败时打印的信息。 self.assertEqual(j.add(),15,msg="测试结果不等于15")
格式:-assertNotEqual(first,second,msg=None)则与之相反
2.判断第一个参数是否在第二个参数中,简言之也就是检查第二个参数是否包含第一个参数
-assertIn(first,second,msg=None) -assertNotIn(first,second,msg=None)
实例:
def test_case(self):a = "hello"b = "hello world"self.assertIn(a,b,msg="a is not in b")
案例:
calc.py
class count:def __init__(self, a, b):self.a = aself.b = b # 计算加法def add(self):return self.a + self.b # 计算减法def Subtraction(self):return self.a - self.b
test.py
import unittest from day06.calc import count """ 测试用例类 需要继承unittest.TestCase, 表明了后面处理的是测试用例 一个方法对应一条测试用例 测试用例所对应的方法名要以test开头 self.assertEqual(a, 4):判断连个值是否相等 """ # 测试用例类 class CountTest(unittest.TestCase): # 编程测试加法的测试用例def test_add01(self):# 先进行类的实例化c = count(5, 4)# 调用实例方法s = c.add()# 断言:判断实际结果和预期结果是否一致self.assertEqual(s, 10, msg="测试不通过") def test_add02(self):# 先进行类的实例化c = count(5, 4)# 调用实例方法s = c.add()# 断言:判断实际结果和预期结果是否一致self.assertNotEqual(s, 10, msg="测试不通过") # 测试减法def test_Subtraction(self):# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) def test_003(self):a = "张无忌"names = ["周芷若", "小昭", "赵敏", '张无忌']# 断言,第一个值 是否 包含在第二个值里面, 如果第一个值在第二个值里面则测试通过self.assertIn(a, names) if __name__ == '__main__':# 执行测试类中的测试用例CountTest.main()
五.测试用例的组织
1.初始化和清除
fixtures可以形象的看作是夹心饼干外层的两片饼干,这两片饼干就是setUp/tearDown,中间的心就是测试用例,除此以外,unittest还提供了更大范围的fixtures,例如对于测试类和模块的fixtures。
setUpModule/tearDownModule:在整个模块的开始和结束时被执行。
setUpClass/tearDownClass: 在测试类的开始和结束时被执行。
setUp/tearDown:在测试用例的开始与结束时被执行 注意:setUpClass/tearDownClass的写法稍有不同,首先通过@classmethod进行装饰,其次方法的参数为cls,也可以是别的。每一个上面都要进行装饰
calc.py
class count:def __init__(self, a, b):self.a = aself.b = b # 计算加法def add(self):return self.a + self.b # 计算减法def Subtraction(self):return self.a - self.b
test.py面试题
import unittest from day06.calc import count """ 初始化和清除:是对测试环境数据的初始化和清除,我们的前置条件准备就可以放在初始化中,本次测试产生的数据进行删除的操作就可以放在清除中 初始化和清除就类似于一个夹心饼干,把我们测试用例夹在中间 初始化和清除有三种方式: 1.方法级别的初始化和清除:使用 setUp tearDown 完成,执行规则是:每条用例开始之前和结束之后分别执行一次 2.类级别的初始化和清除:在这个类开始之前执行初始化,该类中用例执行结束后,执行清除, 3.模块级别的初始化和清除:在这个模块开始之前执行初始化,结束之后执行清除 """ #3. 模块级别的初始化 def setUpModule():print("----------------我是模块级别的初始化-------------") # 这是模块级别的清除 def tearDownModule():print("----------------我是模块级别的清除-------------") # 测试用例类 class CountTest(unittest.TestCase): # 2.类级别的初始化@classmethoddef setUpClass(cls) -> None:print("----------------我是类级别的初始化-------------") # 类级别的清除@classmethoddef tearDownClass(cls) -> None:print("----------------我是类级别的清除-------------") # 1.方法级别的初始化:def setUp(self):print("----------------我是方法级别定位的初始化-------------") # 方法级别的清除def tearDown(self):print("----------------我是方法级别定位的清除-------------") # 编程测试加法的测试用例def test_add01(self):print("----------------我是test_add01-------------")# 先进行类的实例化c = count(5, 4)# 调用实例方法s = c.add()# 断言:判断实际结果和预期结果是否一致self.assertEqual(s, 9, msg="测试不通过") # 测试减法def test_Subtraction(self):print("----------------我是test_Subtraction-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) def test_003(self):print("----------------我是test_003-------------")a = "张无忌"names = ["周芷若", "小昭", "赵敏", '张无忌']# 断言,第一个值 是否 包含在第二个值里面, 如果第一个值在第二个值里面则测试通过self.assertIn(a, names) class CountTest01(unittest.TestCase): # 测试减法def test_004(self):print("----------------test_004-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) # 测试减法def test_005(self):print("----------------test_005-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) if __name__ == '__main__':"""当类中代码缩起来的时候,如果选择在类名后面执行,则会运行当前模块中所有的测试用例但是如果类中的代码没有缩起来,要放在类名后面执行,则执行的该类中的所有测试用例如果要执行的只是某条测试用例,则执行放在测试用例方法名后面,右键执行即可"""# 执行测试类中的测试用例CountTest.main()
1.整个模块开始执行setUpModule,结束后执行tearDownModule
2.CountTest这个类开始执行setUpclass,结束执行tearDown,这个类包括了test_001、test_002、test_003
3.方法级别的初始化和清除:使用 setUp tearDown 完成
2.创建测试套件
测试套件是用例的集合,在实际测试工作中,经常会听说冒烟测试、全量测试、回归测试等测试版本,每个版本所选取的测试用例也是不一样,
如果测试用例数量比较多,可以将这些用例按照所测试的功能进行拆分,分散到不同的测试文件中 最后再创建用于执行所有测试用例的runtest.py文件。
方法1:可以通过addTest()加载TestCase到TestSuite中。用于少量的测试用例
from test import CountTest import unittest #创建套件 suite = unittest.TestSuite() #方法一:添加单个测试用例到套件之中 suite.addTest(CountTest('test_add')) suite.addTest(CountTest('test_subtraction')) runner = unittest.TextTestRunner() runner.run(suite)
calc.py
class count:def __init__(self, a, b):self.a = aself.b = b # 计算加法def add(self):return self.a + self.b # 计算减法def Subtraction(self):return self.a - self.b
test.py
import unittest from day06.calc import count """ 初始化和清除:是对测试环境数据的初始化和清除,我们的前置条件准备就可以放在初始化中,本次测试产生的数据进行删除的操作就可以放在清除中 初始化和清除就类似于一个夹心饼干,把我们测试用例夹在中间 初始化和清除有三种方式: 1.方法级别的初始化和清除:使用 setUp tearDown 完成,执行规则是:每条用例开始之前和结束之后分别执行一次 2.类级别的初始化和清除:在这个类开始之前执行初始化,该类中用例执行结束后,执行清除, 3.模块级别的初始化和清除:在这个模块开始之前执行初始化,结束之后执行清除 """ # 模块级别的初始化 def setUpModule():print("----------------我是模块级别的初始化-------------") # 这是模块级别的清除 def tearDownModule():print("----------------我是模块级别的清除-------------") # 测试用例类 class CountTest(unittest.TestCase): # 类级别的初始化@classmethoddef setUpClass(cls) -> None:print("----------------我是类级别的初始化-------------") # 类级别的清除@classmethoddef tearDownClass(cls) -> None:print("----------------我是类级别的清除-------------") # 方法级别的初始化:def setUp(self):print("----------------我是方法级别定位的初始化-------------") # 方法级别的清除def tearDown(self):print("----------------我是方法级别定位的清除-------------") # 编程测试加法的测试用例def test_add01(self):print("----------------我是test_add01-------------")# 先进行类的实例化c = count(5, 4)# 调用实例方法s = c.add()# 断言:判断实际结果和预期结果是否一致self.assertEqual(s, 9, msg="测试不通过") # 测试减法def test_Subtraction(self):print("----------------我是test_Subtraction-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) def test_003(self):print("----------------我是test_003-------------")a = "张无忌"names = ["周芷若", "小昭", "赵敏", '张无忌']# 断言,第一个值 是否 包含在第二个值里面, 如果第一个值在第二个值里面则测试通过self.assertIn(a, names) class CountTest01(unittest.TestCase): # 测试减法def test_004(self):print("----------------test_004-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) # 测试减法def test_005(self):print("----------------test_005-------------")# 先进行类的实例化c = count(7, 3)# 调用方法a = c.Subtraction()self.assertEqual(a, 4) if __name__ == '__main__':"""当类中代码缩起来的时候,如果选择在类名后面执行,则会运行当前模块中所有的测试用例但是如果类中的代码没有缩起来,要放在类名后面执行,则执行的该类中的所有测试用例如果要执行的只是某条测试用例,则执行放在测试用例方法名后面,右键执行即可"""# 执行测试类中的测试用例CountTest.main()
run.py方法1:可以通过addTest()加载TestCase到TestSuite中。用于少量的测试用例
# 创建测试套件 # 测试套件就类似于测试单,里面可以加载很多用例 # 测试用例执行之后,一个.表示一条测试用例通过了 #方法1:可以通过addTest()加载TestCase到TestSuite中。用于少量的测试用例 import unittest from day06.test import CountTest01 # 创建测试套件 suite = unittest.TestSuite() # 给创建的测试套件中添加测试用例 suite.addTest(CountTest01("test_004")) suite.addTest(CountTest01("test_005")) # 创建执行器,用于执行 创建的测试套件 textRun = unittest.TextTestRunner() # 执行套件 textRun.run(suite)
方法2:添加整个类中的测试用例到套件中
#CountTest是一个测试用例类 from test import CountTest import unittest #创建套件 suite = unittest.TestSuite() #将CountTest类中所有用例添加到套件 suite.addTests(unittest.TestLoader().loadTestsFromTestCase(CountTest)) #创建运行器 runner = unittest.TextTestRunner() #执行套件 runner.run(suite)
案例:
# 创建测试套件 # 测试套件就类似于测试单,里面可以加载很多用例 # 测试用例执行之后,一个.表示一条测试用例通过了 # 方法二:添加整个类中的测试用例到套件中(用于测试用例少的) import unittest from day06.test import CountTest01, CountTest # 创建测试套件 suite = unittest.TestSuite() # 给创建的测试套件中添加测试用例 # 将CountTest01类中所有测试用例全部加载出来 s1 = unittest.TestLoader().loadTestsFromTestCase(CountTest01) suite.addTests(s1) s2 = unittest.TestLoader().loadTestsFromTestCase(CountTest) suite.addTests(s2) # 创建执行器,用于执行 创建的测试套件 textRun = unittest.TextTestRunner() # 执行套件 textRun.run(suite)
方法3:*模糊匹配给套件指定需要执行的测试用例(常用)
使用TestLoader类提供的discover()方法来加载所有的测试用例。正常情况下,不需要创建这个类的实例,unittest提供了可以共享的defaultTestLoader类,可以使其子类和方法创建实例,discover()方法就是其中之一。
discover(start_dir,pattern='test*.py',top_level_dir=None)
start_dir:要测试的模块名或测试用例的目录
pattern='test.py':表示用例文件名的匹配原则,此处文件名以“test”开头的“.py”类型的文件,“”表示任意多个字符。
top_level_dir=None:测试模块的顶层目录,如果没有顶层目录,默认为None.
#指定路径 test_dir = './' #指定项目根目录下,所有的以tes开头的.py文件中的测试用例 discover = unittest.defaultTestLoader.discover(start_dir=test_dir, pattern='tes*.py') #创建运行器 runner = unittest.TextTestRunner() #执行套件 runner.run(discover)
# 创建测试套件 # 测试套件就类似于测试单,里面可以加载很多用例 # 测试用例执行之后,一个.表示一条测试用例通过了 # 方法三:模块匹配 import unittest """ 会采用模块匹配的方式加载出测试用例,直接生成测试套件 start_dir:从那个位置去寻找 写的是路径 pattern="tes*.py 去找tes开头,.py结尾的模块文件中寻找测试用例 """ # 加载用例生成测试套件 discover = unittest.defaultTestLoader.discover(start_dir="./day06", pattern="tes*.py") # 执行测试套件 # 生成执行器 runn = unittest.TextTestRunner() # 执行测试套件 runn.run(discover)
3.用例执行的原则
unittest框架默认根据ASCII码的顺序加载测试用例,数字与字母的顺序为:0-9,A-Z,a-z。所以TestAdd会优于TestBdd类被执行,test_aaa()方法会优于test_ccc被执行,因而它并没有按照用例从上到下的顺序执行。
对于测试目录和测试文件来说,unittest框架同样是按照这个规则来加载测试用例的。 如果按照指定的顺序执行,可以通过TestSuite类的addTest()方法按照一定的顺序加载。不能默认main()方法了。需要构造测试集,然后通过run()方法执行测试。
注意:discover()的加载测试用例的规则与main()方法相同,所以只能通过测试用例的命名来提高被执行的优先级。
4.执行多级的用例
discover()方法中的start_dir只能加载当前目录下的.py文件,如果加载子目录下的.py文件,需在每个子目录下放一个init.py文件。
5.跳过测试和预期失败
@unittest.skip(reason):无条件的跳过装饰的测试,说明跳过测试的原因
六.测试报告生成
1.HTMLTestRunner介绍
HTMLTestRunner是python标准库unittest单元测试框架的一个拓展,它生成易于使用的HTML测试报告。HTMLTestRunner下载地址:HTMLTestRunner - tungwaiyip's software,在GitHub上也有一个人在这个基础上做过改动的,可以自己去下载即可 下载下来是一个HTMLTestRunner.py文件,选中后单击鼠标右键,在弹出的快捷菜单中选择目标另存为,将它保存到本地。安装方法是将其复制到python安装目录下即可。
windows:将下载的文件保存到...\python36\Lib目录下
HTMLTestRunner.py文件是基于python2开发的,若要支持python3环境需要对其中的部分内容进行修改。目前可以在GitHub上找到可供python3使用已经修改过的版本
2.生成HTML测试报告
将HTMLTestRunner模块用import导入进来
通过open()方法以二进制写模式打开当前目录下的result.html,如果没有,则自动创建该文件。
调用HTMLTestRunner模块下的HTMLTestRunner类,stream指定测试报告文件,title用于定义测试报告的标题, description用于定义测试报告的副标题。
最后,通过HTMLTestRunner的run()方法来运行测试套件中所组装的测试用例。
通过close()关闭测试报告文件。
测试报告名字设置
在每次运行测试之前,都要手动修改报告的名称,如果忘记修改,就会把之前的报告覆盖,为了使每次生成的报告名称都不重复并且有意义,可以在报告名称中加入当前时间,这样生成的报告既不会重叠,又能更清晰的知道报告的生成时间。
time.time():获取当前时间戳 比如:1601886309.2652433
time.ctime():当前时间的字符串形式 比如:'Mon Oct 5 16:25:31 2020'
time.localtime():当前时间的struct_time形式 比如:time.struct_time(tm_year=2020, tm_mon=10, tm_mday=5, tm_hour=16, tm_min=25......等等。
time.strftime("%Y-%m-%d %H:%M:%S"):用来获得当前时间,可以将时间格式化为字符串。比如:'2020-10-05 16:27:17'
方法:通过时间操作的方法以指定的格式获取当前时间,将当前时间的字符串赋值给rtime变量,将rtime通过字符串格式化操作拼接到生成的测试报告的文件名中,再次运行测试用例,即可生成测试报告文件名。
import unittest, os, time from webDriver.driver_factory import DriverFactory from common.HTMLTestRunner_cn import HTMLTestRunner report_title = '冒烟测试' report_desc = '本次测试描述' report_path = './report/' rtime = time.strftime("%Y%m%d%H%M%S") #创建测试报告存放的位置和名字 report_file = report_path + f'report{rtime}.html' # #判断项目中是否有report_path所对应的目录,如果没有则新建。有了直接存放结果即可 if not os.path.exists(report_path):os.mkdir(report_path) else:pass #添加用例到套件,采用模糊匹配,在test_case目录下查找 test_dir = './test_case' #指定项目根目录下,所有的以tes开头的.py文件中的测试用例 discover = unittest.defaultTestLoader.discover(start_dir=test_dir, pattern='test_*.py') #执行套件内容,将结果写入 with open(report_file, 'wb') as report:#生成运行器runner = HTMLTestRunner(stream=report, title=report_title, description=report_desc)#执行套件runner.run(discover) #测试套件执行结束关闭浏览器 DriverFactory.driver.quit()
common包:放生成测试报告模块,HTMLTestRunner模块
report文件夹:用来存放生成的测试报告
run.py:执行器,后期不用改动,一直拿来用
test_case:存放测试用例
pages:写测试用例的方法
WebDriver:存放单例模式---判断是否登录
util:放访问地址设置
认识unittest框架,学习unittest框架的使用相关推荐
- 框架学习——WCF框架
框架学习--WCF框架 什么是WCF .NET平台下,有很多分布式技术 WCF 快速搭建一个WCF程序 1.ABC概念 2.如何定义WCF项目 参考资料 什么是WCF .NET平台下,有很多分布式技术 ...
- Spring框架学习day_01: 框架配置方式/ 管理对象的作用域/ 生命周期/ 组件扫描/ 单例模式:“懒汉式“,“饿汉式“
1. Spring框架的作用 Spring框架的主要作用是创建对象和管理对象. 创建对象:类似于User user = new User(); 管理对象:随时可以通过Spring框架获取对象,甚至Sp ...
- Yii 框架学习--01 框架入门
Yii 是一个高性能的,适用于开发 WEB2.0 应用的 PHP 框架. Yii目前有两个主要的版本: 2.0 和 1.1.本文以YII 2.0.7为例. 环境需求 Yii2.0 框架有一些系统上的需 ...
- 框架学习:框架是什么以及框架怎么学
框架就是framwork,百度上说框架是整个或部分系统的可重用性设计,是应用开发者定制的应用骨架.我个人认为框架是一系列的底层服务,让应用开发者调用这一系列的底层服务来快速开发他们想要做的应用程序. ...
- 【Python】flask框架学习 flask框架的基本使用
flask框架是什么? Flask 是一个轻量级的 Web 框架,用于构建 Web 应用程序.它基于 Python 编程语言和 Werkzeug 工具包,提供了简单易用的 API,可以轻松地创建 RE ...
- SpringMVC框架 学习DAY_01:框架概括 / 简易应用 / 核心执行流程图 /在框架下显示HTML模板页面/ 接受请求
1. SpringMVC框架的作用 MVC = Model(数据模型) + View(视图) + Controller(控制器) SpringMVC框架主要解决了接收请求与处理响应的问题,也可以认为是 ...
- NodeJS框架学习-Egg框架
第一步:下载安装nodejs 下载官方网址:https://nodejs.org/en/download/ 本人是windows系统所以如图选择: 下载安装十分方便,环境变量也自动配置完成. 第二步: ...
- unittest 框架学习
为什么80%的码农都做不了架构师?>>> 今天我学习了,怎么把testcase分到不同的文件中,然后在集中到一个testsuite中一起跑,我还学会了怎么打包,怎么引用. 打包 ...
- selenium + python自动化测试unittest框架学习(二)
1.unittest单元测试框架文件结构 unittest是python单元测试框架之一,unittest测试框架的主要文件结构: File >report >all_case.py &g ...
- selenium + python自动化测试unittest框架学习(一)selenium原理及应用
unittest框架的学习得益于虫师的<selenium+python自动化实践>这一书,该书讲得很详细,大家可以去看下,我也只学到一点点用于工作中,闲暇时记录下自己所学才能更加印象深刻. ...
最新文章
- 刷新UITableView
- 2!=5 or 0在python中是否正确-Python 中的 or and 运算,看这一篇就够
- 单点登录与权限管理本质:cookie安全问题
- jenkins 添加 k8s 云
- Java数据结构Map遍历和排序
- 存储过程提示data truncation_手机DATA重新分区教程(超详细)
- 推荐系统常用的推荐算法
- Eclipse快捷键生成语句
- java中的@override
- C#编程(十九)----------部分类
- 设计模式(十七)—— 迭代器模式
- unity中程序的延时
- SPSS统计分析学习记录
- VoIP服务器曝重大漏洞,黑客可以绕过管理员身份
- 家庭监控方案设计及施工-无线监控
- 谷歌浏览器插件离线安装
- vue-element-template模板添加登录页面自定义背景
- 瑞芯微Rockchips RK3368对比晶晨Amlogic S905
- 疫情之下,从一座空城,到另一座空城,第一次看到这么寂寞的杭州
- 安卓内存数据分析和内存脚本分析教程分享
热门文章
- Java在hdfs上创建新文件夹遇错误:Permission denied: user=atguigu, access=READ_EXECUTE, inode=“/tmp“:root:supe
- CTR预估模型演进史 · DeepCross、DIN、ESMM
- 五个可以避免的BYOD错误
- 匈牙利算法:二分图的最大匹配
- 什么是static?
- 结巴分词1.8.2版本源代码解析(一)
- 生男生女真的那么重要吗?
- 由作弊引发的对规则的思考
- 【数理统计】极大似然估计
- python身份证城市定位程序输入点_有关一道身份证的python编程题