自动化测试unittest框架
目录
- 本章目标
- unittest框架解析
- 新窗口,无法定位元素
- 批量执行脚本
- - `addTest()`
- 构造测试套件
- makeSuite()和TestLoader的应用
- discover()的引用
- 用例的执行顺序
- 忽略用例执行
- unittest断言
- verbosity
- HTML报告生成
- 异常捕捉和错误截图
- 数据驱动
- **ddt使用方法:**
本章目标
本章主要是学习单元测试框架
untittest
,这里的单元测试指的是对最小的软件设计单元(模块)进行的测试验证,在UI
自动测试里,这些单元测试主要针对的是UI
界面的功能进行的自动化测试.这里的unittest
并不是java里的unit
单元测试框架!
- unittest框架解析
- 批量处理脚本
- unittest断言
- HTML报告生成
- 异常捕捉和错误截图
- 数据驱动
unittest框架解析
unittest
是python的单元测试框架,主要作用如下:
- 提供用例的组织和执行
当我们的测试用例比较庞大时就需要考虑用例的组织和执行顺序,unittest就可以帮助我们!
- 提供丰富的比较方法
我们用例执行结束后要通过比较用例执行的实际结果和预期结果(断言),从而确定用例是否顺利通过!
例如判断相等/不相等,包含/不包含,True/False
- 提供丰富的日志
当测试用例执行失败时可以清晰的抛出和保存用例执行失败的原因!日志提供了丰富的执行结果:用例总执行时间,失败用例数,成功用例数
unittest里的四个重要概念:
- Test Fixture
对一个测试环境的搭建和销毁就是一个
fixture
,主要通过setUp()
和tearDown()
覆盖方法实现!
setUP()
:对测试环境的搭建,比如获取浏览器驱动,数据库建立连接初始化等等tearDown()
: 对测试环境的销毁,比如关闭浏览器,关闭数据库的连接,清楚数据库中的数据等
- Test Case
Test Case的实例就是一个测试用例.测试用例就是完整的一个测试流程,包括测试环境的搭建(setUp),实现测试过程的代码,环境的还原(tearDown).一个测试用例就是一个测试单元,可以对某个测试功能进行验证!
- Test Suite
一个功能的验证往往需要多个测试用例,可以把多个测试用例集合在一起执行,这个就产生了测试套件TestSuite的概念。Test Suit测试套件用来将多个测试用例组装在一起
- Test Runner
测试的执行也是非常重要的一个概念,在unittest框架中,通过TextTestRunner类提供的run()方法来执行test suite/test case。
unittest框架测试脚本示例:
# 导入unittest框架
import unittest
from selenium import webdriver
import time
# 继承TestCase这个类就会使用unittest框架来组织测试用例!
class Baidu1(unittest.TestCase):def setUp(self) -> None: # 覆盖setUP方法,测试环境搭建!print('---setUp---')self.driver = webdriver.Chrome() # 获取到浏览器驱动self.url = "http://www.baidu.com/" #测试的web链接self.driver.maximize_window() # 浏览器最大化!time.sleep(3)def tearDown(self) -> None: # 覆盖tearDown方法,测试环境销毁!print('---tearmDown---')self.driver.quit() # 关闭浏览器,释放驱动!# 测试用例def test_baidu1(self):self.driver.get(self.url) # 打开百度链接self.driver.find_element_by_id('kw').send_keys('java') # 定位到搜索框,输入文本time.sleep(2)self.driver.find_element_by_id('su').click() # 定位到搜索按钮点击!time.sleep(3)def test_baidu2(self):self.driver.get(self.url) # 重新打开百度链接!self.driver.find_element_by_link_text('新闻').click() # 打开新闻导航地址time.sleep(2)for window in self.driver.window_handles:self.driver.switch_to_window(window)if window.title =='百度新闻——海量中文资讯平台':breakself.driver.find_element_by_name('word').send_keys('赣州')time.sleep(2)self.driver.find_element_by_xpath('//*[@id="s_btn_wr"]').click()time.sleep(2)#滑动到低端!js = 'var j = document.documentElement.scrollTop=10000'self.driver.execute_script(js)time.sleep(3)#滑动到顶端js = 'var j = document.documentElement.scrollTop=0'self.driver.execute_script(js)time.sleep(3)def baidu3(self):self.driver.get('http://baidu.com/')self.driver.find_element_by_partial_link_text('hao').click()time.sleep(2)if __name__=='__main__':unittest.main() # unittest框架提供了全局的main方法
这里
unittest
提供了全局的main
方法,main
方法中会将test_
开头的方法进行组织,然后调用main
方法就可以执行所有的以test_
开头的方法!
新窗口,无法定位元素
注意事项
当我们在当前页面的基础上开了一个新的窗口页面,我们如果要对新的窗口元素进行定位,我们需要将窗口更新,才能定位到新的窗口元素
# 进行窗口定位之前要time.sleep() 保证窗口已经跳转过来!
for handle in self.driver.window_handles:self.driver.switch_to_window(handle) # 转换到新的窗口!if hanle.title =='新窗口title':break # 定位到窗口就退出循环!
#进行新窗口元素的定位!
批量执行脚本
但我们需要扩展新的功能测试或者测试用例时,我们需要把多个测试用例组织在一起执行,我们可以通过
unittest
中的测试套件Test Suite
进行测试用例的组织
假设我们编写了2个文件test1.py
和test2.py
我们如何同时执行这2个文件呢?
test1.py
# 导入unittest框架
import unittest
from selenium import webdriver
import time
# 继承TestCase这个类就会使用unittest框架来组织测试用例!
class Baidu1(unittest.TestCase):def setUp(self) -> None: # 覆盖setUP方法,测试环境搭建!print('---setUp---')self.driver = webdriver.Chrome() # 获取到浏览器驱动self.url = "http://www.baidu.com/" #测试的web链接self.driver.maximize_window() # 浏览器最大化!time.sleep(3)def tearDown(self) -> None: # 覆盖tearDown方法,测试环境销毁!print('---tearmDown---')self.driver.quit() # 关闭浏览器,释放驱动!# 测试用例def test_baidu1(self):self.driver.get(self.url) # 打开百度链接self.driver.find_element_by_id('kw').send_keys('java') # 定位到搜索框,输入文本time.sleep(2)self.driver.find_element_by_id('su').click() # 定位到搜索按钮点击!time.sleep(3)def test_baidu2(self):self.driver.get(self.url) # 重新打开百度链接!self.driver.find_element_by_link_text('新闻').click() # 打开新闻导航地址time.sleep(2)for window in self.driver.window_handles:self.driver.switch_to_window(window)if window.title =='百度新闻——海量中文资讯平台':breakself.driver.find_element_by_name('word').send_keys('赣州')time.sleep(2)self.driver.find_element_by_xpath('//*[@id="s_btn_wr"]').click()time.sleep(2)#滑动到低端!js = 'var j = document.documentElement.scrollTop=10000'self.driver.execute_script(js)time.sleep(3)#滑动到顶端js = 'var j = document.documentElement.scrollTop=0'self.driver.execute_script(js)time.sleep(3)def baidu3(self):self.driver.get('http://baidu.com/')self.driver.find_element_by_partial_link_text('hao').click()time.sleep(2)if __name__=='__main__':unittest.main() # unittest框架提供了全局的main方法
test2.py
from selenium import webdriver
import unittest
import time
from selenium.webdriver.common.action_chains import ActionChains
class test2(unittest.TestCase): # 继承 TestCasedef setUp(self) -> None:# 测试环境搭建print('--test2.setUp--')self.driver = webdriver.Chrome() #获取驱动self.url = 'https://fanyi.baidu.com/?aldtype=16047#auto/zh' # 百度翻译的url!def tearDown(self) -> None:#测试环境销毁print('--test2.tearDown--')self.driver.quit()#测试用例!def test_baidu1(self):self.driver.get(self.url) # 打开百度翻译url!self.driver.maximize_window() # 窗口最大化!self.driver.find_element_by_id('baidu_translate_input').send_keys('unittest') # 定位到翻译搜索框time.sleep(3)# 定位到弹出的广告!self.driver.find_element_by_xpath('//*[@id="app-guide"]/div/div/div[2]/span').click()time.sleep(2)self.driver.find_element_by_id('translate-button').click() # 定位到翻译按钮time.sleep(3)# 定位到翻译结果框!test = self.driver.find_element_by_xpath('/html/body/div[1]/div[2]/div/div/div[1]/div[2]/div[1]/div[2]/div/div/div[1]/p[2]').textprint('unitest:',test)def test_baidu2(self):self.driver.get(self.url)# 定位到弹出的广告!self.driver.find_element_by_xpath('//*[@id="app-guide"]/div/div/div[2]/span').click()time.sleep(2)p = self.driver.find_element_by_xpath('/html/body/div[1]/div[1]/div/div/div/div[1]/div/span') # 定位到文档翻译# 将鼠标移动到该位置!ActionChains(self.driver).move_to_element(p).perform()time.sleep(2)# 然后定位到上传文档按钮!self.driver.find_element_by_xpath('//*[@id="header"]/div/div/div/div[1]/div/span').click()time.sleep(3)if __name__ == '__main__':unittest.main()
- addTest()
TestSuite类中的
addTest
方法可以把不同类的测试用例方法组织到测试套件中,但是这里的addTest
方法一次只能添加一个测试用例方法
这里的不足就是每次只能添加一个方法到套件中,并且要该方法的类导入
构造测试套件
makeSuite()和TestLoader的应用
- makeSuite()
在
unittest
框架中提供了makeSuite()
方法,makeSuite
可以将测试用例类内的测试用例case组成套件TestSuite
unittest
调用makeSuite
传入类名即可!
- TestLoader()
TestLoader
用于创建类和模块的测试套件,一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass)
来加载测试类。
下面就是组织成测试套件的3种方式
import unittest
from unitest_ import test1
from unitest_ import test2
def createsuite():# addTest # 向测试套件里添加测试用例!# suite = unittest.TestSuite()# suite.addTest(unitest_.test1.Baidu1('test_baidu1'))# suite.addTest(unitest_.test1.Baidu1('test_baidu2'))# suite.addTest(unitest_.test2.test2('test_baidu1'))# suite.addTest(unitest_.test2.test2('test_baidu2'))# return suite# makeSuite 通过addTest()方法,将一个类的的测试用例通过makeSuite添加# suite = unittest.TestSuite()# suite.addTest(unittest.makeSuite(test1.Baidu1))# suite.addTest(unittest.makeSuite(test2.test2))# return suite#通过TestLoader将测试用例组织成测试套件!#先加载s1 = unittest.TestLoader().loadTestsFromTestCase(test1.Baidu1)s2 = unittest.TestLoader().loadTestsFromTestCase(test2.test2)suite = unittest.TestSuite([s1,s2])return suite
if __name__=='__main__':suit = createsuite()runner = unittest.TextTestRunner(verbosity=2)runner.run(suit)
上面的3种只能导入一个类,当我们不用一个
.py
测试类,我们只需要导入一个方法该如何操作呢?
discover()的引用
discover
是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们
对象的TestSuite
,然后进行加载与模式匹配唯一的测试文件,discover
参数分别为discover(dir,pattern,top_level_dir=None)
import unittest
from unitest_ import test1
from unitest_ import test2
def createsuite():discover = unittest.defaultTestLoader.discover('../test1',pattern='test*.py',top_level_dir=None)print(discover)return discover
if __name__=='__main__':suit = createsuite()runner = unittest.TextTestRunner(verbosity=2)runner.run(suit)
用例的执行顺序
unittest
框架默认加载测试用例的顺序是根据ASCII
码的顺序,数字与字母的顺序为: 0~ 9 ,A~ Z,a~z 。所以,TestAdd
类会优先于TestBdd
类被发现,test_aaa()
方法会优先于test_ccc()
被执行。对于测试
目录与测试文件来说,unittest
框架同样是按照这个规则来加载测试用例。
addTest()方法按照增加顺序来执行
忽略用例执行
我们通过
unittest
下的一个注解实现测试用例的忽略功能!
@unitest.skip('忽略用例的注释')
运行结果
unittest断言
自动化的测试中, 对于每个单独的case来说,一个case的执行结果中, 必然会有期望结果与实际结果, 来判断该case是通过还是失败, 在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果, 一般来说, 检查条件大体分为等价性, 逻辑比较以及其他, 如果给定的断言通过, 测试会继续执行到下一行的代码, 如果断言失败, 对应的case测试会立即停止或者生成错误信息( 一般打印错误信息即可) ,但是不要影响其他的case执行。
unittest 的单元测试库提供了标准的xUnit 断言方法。下面是一些常用的断言:
我们期待的结果是测试框架
但是实际结果test
是单元测试,那就说明这条测试用例没有通过!
verbosity
verbosity是一个选项,表示测试结果的信息复杂度,有0、1、2三个值。
0:静默模式,只能获取总的测试数和总的执行结果,比如成功3,失败4
1:默认模式,非常类似静默模式,只是在每个成功的测试用例前面会有".“,在每个失败的测试用例前面有"F”
2:详细模式,测试结果会显示每个测试用例的所有相关的信息
verbosity=0
verbosity=1
verbosity=2
HTML报告生成
脚本执行完毕之后,还需要看到HTML报告,下面我们就通过HTMLTestRunner.py 来生成测试报告。
HTMLTestRunner
支持python2.7。python3可以参见http://blog.51cto.com/hzqldjb/1590802来进行修改。
HTMLTestRunner.py
文件,下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
下载后将其放在testcase目录中去或者放入...\Python27\Lib 目录下(windows)
。
import HTMLTestRunner
import os
import sys
import time
import unittestdef createsuite():discovers = unittest.defaultTestLoader.discover("../unitest_", pattern="test*.py", top_level_dir=None)print(discovers)return discoversif __name__=="__main__":# 文件夹要创建在哪里curpath = sys.path[0]print(sys.path)print(sys.path[0])# 1,创建文件夹,创建的这个文件夹干什么if not os.path.exists(curpath+'/resultreport'):os.makedirs(curpath+'/resultreport')# 2,文件夹的命名,不能让名称重复# 时间 时分秒 ——》名称绝对不会重复now = time.strftime("%Y-%m-%d-%H %M %S", time.localtime(time.time()))print(now)print(time.time())print(time.localtime(time.time()))# 文件名filename = curpath + '/resultreport/'+ now + 'resultreport.html'with open(filename, 'wb') as fp:runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u"测试报告",description=u"用例执行情况", verbosity=2)suite = createsuite()runner.run(suite)
异常捕捉和错误截图
用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。
例如编写一个函数,关键语句为driver.get_screenshot_as_file:
def savescreenshot(self,driver,file_name):if not os.path.exists('./image'):os.makedirs('./image')now=time.strftime("%Y%m%d-%H%M%S",time.localtime(time.time()))#截图保存driver.get_screenshot_as_file('./image/'+now+'-'+file_name)time.sleep(1)
数据驱动
之前我们的case都是数据和代码在一起编写。考虑如下场景:需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这时候需要编写3个案例吗?有没有版本一次运行?
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT
来完成。
DDT安装地址:
https://ddt.readthedocs.io/en/latest/
在cmd
命令窗口输入下面命令即可!
pip install ddt
python setup.py install
可能
pip
版本不够,可以使用版本更新命令
查看当前pip
版本
pip show pip
更新pip
版本
python -m pip install --upgrade pip
安装成功
ddt使用方法:
参考文档:http://ddt.readthedocs.io/en/latest/
dd.ddt:
装饰类,也就是继承自TestCase的类。
ddt.data:
装饰测试方法。参数是一系列的值。
ddt.file_data:
装饰测试方法。参数是文件名。文件可以是json 或者 yaml类型。
注意,如果文件以”.yml”或者”.yaml”结尾,ddt会作为yaml类型处理,其他所有文件都会作为json文件处理。
如果文件中是列表,每个列表的值会作为测试用例参数,同时作为测试用例方法名后缀显示。
如果文件中是字典,字典的key会作为测试用例方法的后缀显示,字典的值会作为测试用例参数。
ddt.unpack:
传递的是复杂的数据结构时使用。比如使用元组或者列表,添加unpack之后,ddt会自动把元组或者列表对应到多个参数上。字典也可以这样处理。
下面代码学习来源:http://t.zoukankan.com/Maruying-p-13516791.html
1、传递列表、字典等数据
# get_ddt.pyimport unittest
from ddt import ddt, data, unpack, file_data# 声明了ddt类装饰器
@ddt
class MyddtTest(unittest.TestCase):# @data方法装饰器# 单组元素@data(1,2,3)def test_01(self, value): # value用来接受data的数据print(value)# 多组数据,未拆分@data([1,2],[3,4])def test_02(self, value):print(value)# 多组数据,拆分# @unpac拆分,相当于把数据的最外层结构去掉@data([5,6],[7,8])@unpackdef test_03(self, value1, value2):print(value1, value2)# 单个列表字典,未拆分@data([{"name": "peter", "age": 15, "addr": "chengdu"}])def test_04(self, value):print(value)# 多个列表字典,拆分@data([{"name":"peter","age":16,"addr":"chengdu"},{"name":"lily","age":17,"addr":"chengdu"}])@unpackdef test_05(self, value1, value2):print(value1, value2)# 单个字典,拆分# @data里的数据key必须与字典的key保持一致@data({"name":"jack","age":20})@unpackdef test_06(self, name, age):print(name, age)# 多个字典, 拆分@data({"name":"peter","age":18,"addr":"chengdu"},{"name":"lily","age":19,"addr":"chengdu"})@unpackdef test_07(self, name, age, addr):print(name, age, addr)# 多个列表字典,引用数据testdata = [{"name": "peter", "age": 21, "addr": "chengdu"}, {"name": "lily", "age": 22, "addr": "chengdu"}]@data(testdata)@unpackdef test_08(self, value1, value2):print(value1, value2)# @data(*testdata):*号意为解包,ddt会按逗号分隔,将数据拆分(不需要@unpack方法装饰器了)testdata = [{"name":"peter","age":23,"addr":"chengdu"},{"name":"lily","age":24,"addr":"chengdu"}]@data(*testdata)def test_09(self, value):print(value)if __name__ == "__main__":unittest.main()
2.传递json、yaml
文件
3.python
中使用ddt+excel
读取测试数据
自动化测试unittest框架相关推荐
- selenium + python自动化测试unittest框架学习(二)
1.unittest单元测试框架文件结构 unittest是python单元测试框架之一,unittest测试框架的主要文件结构: File >report >all_case.py &g ...
- 自动化测试——unittest框架
自动化测试--unittest框架 文章目录 自动化测试--unittest框架 unittest 一.TestCase(测试用例) 二.TestSuite(测试套件)和TestRunner(测试执行 ...
- 自动化测试——unittest框架(单元测试)
目录 一.unittest框架解析 1.1unittest的5个重要概念 1.1测试用例的编写及代码 1.2断言 1.3用例的执行顺序 1.4测试用例综合管理框架 1.5HTML报告生成 参考博文 一 ...
- selenium + python自动化测试unittest框架学习(一)selenium原理及应用
unittest框架的学习得益于虫师的<selenium+python自动化实践>这一书,该书讲得很详细,大家可以去看下,我也只学到一点点用于工作中,闲暇时记录下自己所学才能更加印象深刻. ...
- selenium原理python_selenium + python自动化测试unittest框架学习(一)selenium原理及应用...
unittest框架的学习得益于虫师的<selenium+python自动化实践>这一书,该书讲得很详细,大家可以去看下,我也只学到一点点用于工作中,闲暇时记录下自己所学才能更加印象深刻. ...
- selenium + python自动化测试unittest框架学习(五)webdriver的二次封装
因为webdriver的api方法很长,再加上大多数的定位方式是以xpath方式定位,更加让代码看起来超级长,为了使整体的代码看起来整洁,对webdriver进行封装,学习资料来源于虫师的<se ...
- selenium自动化测试、Python单元测试unittest框架以及测试报告和日志输出
部分内容来自:https://www.cnblogs.com/klb561/p/8858122.html 一.基础介绍 核心概念:test case, testsuite, TestLoder,Tex ...
- 自动化测试:Selenium8种元素定位+unittest框架设计
作者简介: 笔名,软件测试君.参与过汇丰银行,国家电网,中国电信等多个大型项目的研发和管理,擅长的技术领域为安全测试,性能测试,自动化框架搭建与维护,曾受南京航空航天大学邀请分享Linux.oracl ...
- Selenium自动化测试:8种元素定位+unittest框架设计
作者简介: 笔名,唐米.参与过汇丰银行,国家电网,中国电信等多个大型项目的研发和管理,擅长的技术领域为安全测试,性能测试,自动化框架搭建与维护,曾受南京航空航天大学邀请分享Linux.oracle等测 ...
最新文章
- mybatis一对多关联查询_一对一,一对多,多对多查询及延迟加载(N+1问题)分析
- 互动整合营销_营销成本日益升高,整合营销到底有多重要
- Mono源码学习笔记:Console类(四)
- Express engine 学习笔记 - 工作在反向代理背后的 Express 设置
- python银行排队系统_python-我需要基于Web的系统的消息/排队解决...
- kuboard使用mysql_Kuboard Kubernetes安装
- 再品Resnet残差网络
- Android项目实战(三十一):异步下载apk文件并安装(非静默安装)
- 使用 profile 进行python代码性能分析
- java和javascript双引号嵌套的问题
- 【电路与电子技术】笔记 (完结)
- 对LFW数据库的翻译【1】
- 20个Python实战项目(附源码),拿走就用。
- VUE学习(一)、创建一个Vue应用。
- css3实现爱心图标
- 私有化部署文字识别SDK
- 【NLP】自然语言处理学习笔记(一)语音识别
- 在计算机中有什么作用,内存是什么在电脑中有什么作用
- InputStream 和OutputStream
- Jenkins Gitlab 插件及 Gitlab 全局变量
热门文章
- hadoop报错总结01_李孟_新浪博客
- 计算机组成原理 内容存储器,计算机组成原理实验(存储器).ppt
- HDU5040 Instrusive,bfs+优先队列
- RiPro主题二级菜单添加小圆点图标
- org.hibernate.AssertionFailure:collection[......] was not processed by flush()
- 如何提高百度云客户端的下载速度
- 老猿学5G扫盲贴:3GPP中的5G计费架构
- 【Java】简易视频播放器
- SQL注入,“骇客的填空游戏”
- 九年级物理测试软件,九年级物理解读人教版