目录
一、自动化测试基础
1.1、什么是自动化测试
1.2、自动化测试工具与方法
1.3、自动化测试优势
1.4、如何实施自动化测试
二、selenium 介绍与环境搭建
2.1 selenium 基本介绍
2.2 selenium 工具集
三、selenium API 讲解
3.1 元素的定位
3.2 操作测试对象
3.3添加等待
3.4 打印信息
3.5 浏览器的操作
3.6 键盘事件
3.7鼠标事件
3.8 定位一组元素:checkbox.html
3.9 多层框架/窗口定位:frame.html、inner.html
3.10 层级定位:level_locate.html
3.11 下拉框处理:drop_down.html
3.12 alert、confirm、prompt的处理:alert.html、send.html
3.13 DIV 对话框处理:modal.html
3.14 上传文件操作:upload.html
四、unitest 测试框架
4.1 unittest 框架解析
4.2 批量执行脚本
4.3 unittest 断言
4.4 HTML 报告生成
4.5 异常捕获与错误截图
4.6 数据驱动
完整测试代码:
自动化介绍:自动化测试指软件测试的自动化,在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结果。将人为驱动的测试行为转化为机器执行的过程。
1、常见自动化测试工具:
2、selenium 的诸多优点:
3、自动化测试方法:
测试对象:UI、接口、代码
测试过程:系统测试、集成测试、单元测试
执行人员:测试人员、开发人员
测试时机:功能相对稳定的情况下
- 自动化测试可以在整个测试过程中任何一个阶段实施,前提功能相对稳定
- 测试人员一般在系统测试时进行自动化测试
- 集成测试阶段多进行自动构建、部署,以及冒烟测试的自动化
- 单元测试针对代码级别进行测试,可进行静态代码检查,或者执行单元测试用例,典型的框架比如junit,jmock等,该部分多由开发人员实施
UI 自动化:
测试前提:前端页面完成
- 用例维护量大
- 页面相关性强,必须后期介入
- UI测试适合与界面变动较小的项目
接口自动化:
接口自动化前提:后端开发完成
- 可在产品前期介入
- 用例维护量小
- 页面相关性小
- 适合接口变动较小,界面变动频繁的项目
降低大型系统的由于变更或者多期开发引起的大量的回归测试的人力投入,这可能是自动化测试最主要的任务,特别是在程序修改比较频繁时,效果是非常明显的,自动化测试前期人力投入较多,但后期进入维护期后,可节省大量人力,而手工测试后期需要增加大量人力用于回归测试。自动化脚本的利用率越高,价值越大。回归测试、兼容性测试做成自动化更好。
1、自动化测试适用对象
实施自动化测试的前提条件:需求变动不频繁、项目周期足够长、自动化测试脚本可重复使用
适合做自动化的项目:
2、什么时候实施
建议:可预见的需求不影响自动化测试用例的设计
3、如何实施自动化测试
单纯的讲,自动化测试的具体实现,应该是包含下面七个过程的。
4、自动化测试实施失败的因素
Selenium是ThroughtWorks公司一个强大的开源Web功能测试工具系列,支持多平台、多浏览器、多语言去实现自动化测试,Selenium2将浏览器原生的API封装成WebDriver API,可以直接操作浏览器页面里的元素,甚至操作浏览器本身(截屏,窗口大小,启动,关闭,安装插件,配置证书之类的),所以就像真正的用户在操作一样。
支持多种开发语言:ruby,python,java,perl,c#等,同时Selenium 测试直接自动运行在浏览器中,就像真正的用户在手工操作一样。
支持的浏览器包括 IE、Chrome 和 Firefox等。
1、selenium1 :selenium IDE(录制脚本),selenium RC(控制浏览器的),selenium GRID(分布式)
主要问题:用JavaScript写的有环境沙箱问题,会阻止一些弹窗alert
Selenium IDE
Selenium IDE (集成开发环境) 是一个创建测试脚本的原型工具。它是一个 Firefox 插件,实现简单的浏览器操作的录制与回放功能,提供创建自动化测试的建议接口。Selenium IDE 有一个记录功能,能记录用户的操作,并且能选择多种语言把它们导出到一个可重用的脚本中用于后续执行。
Selenium RC
Selenium RC 是selenium 家族的核心工具,Selenium RC 支持多种不同的语言编写自动化测试脚本,通过selenium RC 的服务器作为代理服务器去访问应用从而达到测试的目的。
selenium RC 使用分Client Libraries 和Selenium Server。
Selenium Grid
Selenium Grid 使得 Selenium RC 解决方案能提升针对大型的测试套件或者哪些需要运行在多环境的测试套件的处理能力。Selenium Grid 能让你并行的运行你的测试,也就是说,不同的测试可以同时跑在不同的远程机器上。这样做有两个有事,首先,如果你有一个大型的测试套件,或者一个跑的很慢的测试套件,你可以使用 Selenium Grid 将你的测试套件划分成几份同时在几个不同的机器上运行,这样能显著的提升它的性能。同时,如果你必须在多环境中运行你的测试套件,你可以获得多个远程机器的支持,它们将同时运行你的测试套件。在每种情况下,Selenium Grid 都能通过并行处理显著地缩短你的测试套件的处理时间。
2、selenium2 :Webdriver(解决环境沙箱问题)
Webdriver原理:驱动浏览器进行操作
3、selenium3 :增加了一些浏览器的原生驱动,edge,Safari,geckodriver
一个简单脚本:
# coding = utf-8 #可加可不加,开发人员喜欢加一下,防止乱码。
from selenium import webdriver #要想使用selenium 的webdriver 里的函数,首先把包导进来
import time
browser = webdriver.Firefox() #我们需要操控的浏览器
time.sleep(3) #等待三秒
browser.get("http://www.baidu.com")
time.sleep(3)
browser.find_element_by_id("kw").send_keys("selenium") #一个控件有若干属性id 、name、(也可以用其它方式定位),百度输入框的id 叫kw ,我要在输入框里输入selenium 。
time.sleep(3)
browser.find_element_by_id("su").click() #搜索的按钮的id 叫su ,我需要点一下按钮( click() )。
browser.quit() #退出并关闭窗口的每一个相关的驱动程序
#brower.close() #关闭当前窗口。
'''
close方法关闭当前的浏览器窗口,quit方法不仅关闭窗口,还会彻底的退出webdriver,释放与driver server之间
的连接。所以简单来说quit是更加彻底的close,quit会更好的释放资源
'''
要操作一个对象,首先应该识别这个对象,就像一个人一样,我们可以通过身份证号,住址等找到他,对象也有类似的属性,但是不管用哪种方式,必须保证页面上该属性的唯一性。
webdriver 提供了一系列的对象定位方法,常用的有以下几种:
查看对象元素方法:在浏览器中找到需要查看的元素,右键点击最下面的检查就可以啦!
如何知道 CSS 与 XPath?右键点击 Copy,就会出现一些内容,选择自己想要的。
元素定位解释:
方法Method | 参数Argument | 示例Example |
id | id: 需要被查找的元素的ID | dr.find_element_by_id('search') |
name | name: 需要被查找的元素的名称 | dr.find_element_by_name('q') |
class name | class_name: 需要被查找的元素的类名 | dr.find_element_by_class_name('input-text') |
tag_name | tag: 需要被查找的元素的标签名称 | dr.find_element_by_tag_name('input') |
link_text | link_text: 需要被查找的元素的链接文字 | dr.find_element_by_link_text('Log In') |
partial_link_text | link_text: 需要被查找的元素的部分链接文字 | dr.find_element_by_partial_link_text('Long') |
xpath | xpath: 需要被查找的元素的xpath | dr.find_element_by_xpath('//*[@id="xx"]/a') |
css_selector | css_selector: 需要被查找的元素的ID | dr.find_element_by_css_selector('#search') |
上面3.1讲述了元素定位,那么定位到元素之后需要对元素进行操作,是进行鼠标点击还是键盘输入,取决于我们定位的是按钮还是输入框。一般来说,webdriver 中比较常用的操作对象的方法有下面几个:
方法 | 描述 | 示例 |
click | 用于点击一个按钮 | brower.find_element_by_id('su').click() |
submit | 提交表单,将"百度一下"的click换成submit效果一样 | brower.find_element_by_id('su').submit() |
clear | 清除输入框内容 | brower.find_element_by_id('kw').clear() |
send_keys | 在一个输入框输入内容 | brower.find_element_by_id('kw').send_keys("CSDN") |
text | 获取元素的文本信息 | browser.find_element_by_id("s-bottom-layer-right").text |
3.1和3.2代码示例:
# coding = utf-8
from selenium import webdriver
import time
browser = webdriver.Chrome() # 获得Google的驱动
time.sleep(2)
browser.get("http://www.baidu.com")
#通过 id 定位到 input 输入框,然后输入待搜索内容:CSDN
'''browser.find_element_by_id("kw").send_keys("CSDN")'''
#搜索的按钮的 id 叫做 su ,我需要点一下按钮 click()
'''browser.find_element_by_id("su").click()'''
# 通过name定位
'''
browser.find_element_by_name("wd").send_keys("网易云音乐")
browser.find_element_by_id("su").click()'''
# 通过链接内容定位
'''
browser.find_element_by_link_text("高考加油").click()
browser.find_element_by_partial_link_text("高考").click()'''
#通过tag name 方式定位, 不能成功,因为input太多了不唯一。
'''browser.find_element_by_tag_name("input").send_keys("selenium")'''
# CSS 定位,id --> #id名,class唯一时 --> .class名
'''
browser.find_element_by_css_selector("#kw").send_keys("hello")
browser.find_element_by_css_selector(".s_ipt").send_keys("hello")
'''
# XPath定位,* 表示将前面的内容全部省略掉,用 id 定位
'''
#browser.find_element_by_xpath("//*[@id='kw']").send_keys("521")
browser.find_element_by_xpath("//*[@name='wd']").send_keys("521")
browser.find_element_by_xpath("//*[@id='su']").click()
'''
# 先在文本框输入 “CSDN",再清除内容,再次输入”元龙“进行搜索
'''
browser.find_element_by_id("kw").send_keys("CSDN")
time.sleep(2)#等待两秒钟,不等待的话会出错,页面加载需要时间
browser.find_element_by_id("kw").clear()
time.sleep(2)
browser.find_element_by_id("kw").send_keys("元龙")
# browser.find_element_by_id("su").click()
browser.find_element_by_id("su").submit()
'''
# 获取元素文本
# print(browser.find_element_by_id("s-bottom-layer-right").text)
添加休眠非常简单,我们需要引入time 包,就可以在脚本中自由的添加休眠时间了
import time
time.sleep(3)
智能等待:implicitly_wait()
通过添加implicitly_wait() 方法就可以方便的实现智能等待;implicitly_wait(30)的用法应该比time.sleep() 更智能,后者只能选择一个固定的时间的等待,前者可以在一个时间范围内智能的等待。
# coding = utf-8
from selenium import webdriver
import time #调入time 函数
browser = webdriver.Chrome()
browser.get("http://www.baidu.com")
browser.implicitly_wait(30) #智能等待30秒
browser.find_element_by_id("kw").send_keys("selenium")
browser.find_element_by_id("su").click()
browser.quit()
打印 title 和 URL,虽然没看到脚本执行过程,但在执行结果可以看到打印的内容,说明页面正确被打开了:
print(driver.title) #百度一下,你就知道
print(driver.current_url) #http://www.baidu.com
1、浏览器最大化:调用启动的浏览器不是全屏的,这样不会影响脚本的执行,但是有时候会影响我们“观看”脚本的执行。
浏览器还可以最小化和设置固定大小。browser.set_window_size( 宽,高)
driver.maximize_window()# 浏览器最大化
#driver.minimize_window()# 浏览器最小化
# time.sleep(2)
# driver.set_window_size(400,600)# 浏览器设置固定大小
2、浏览器的前进与后退:浏览器上有一个后退、前进按钮,对于浏览网页的人是比较方便的
driver.back()#后退
driver.forward()#前进
3、控制浏览器滚动条:execute_script(script, *args),在当前窗口/框架同步执行javaScript
js = "var q=document.documentElement.scrollTop=10000" #滚动条拖到最底端
driver.execute_script(js)
time.sleep(2)
js = "var q=document.documentElement.scrollTop=0" #滚动条拖到最顶端
driver.execute_script(js)
#windows.scollTo(x-coord,y-coord),x-coord 是文档中的横轴坐标,y-coord 是文档中的纵轴坐标。控制浏览器的上下左右
driver.execute_script("window.scrollTo(1000,0)") #滚动条拖到最右端
time.sleep(2)
driver.execute_script("window.scrollTo(0,1000)") #滚动条拖到最左端
1、键盘按键用法:
要想调用键盘按键操作需要引入keys 包:
from selenium.webdriver.common.keys import Keys
通过send_keys()调用按键:
send_keys(Keys.TAB) # TAB
send_keys(Keys.ENTER) # 回车
代码:
driver.get("http://127.0.0.1:81/biz/user-login.html")
driver.implicitly_wait(10)
driver.maximize_window() # 浏览器全屏显示
# TAB键和 Inter 键的用法
#tab 的定位相当于清除了密码框的默认提示信息,等同上面的clear()
driver.find_element_by_id("account").send_keys("admin")
driver.find_element_by_id("account").send_keys(Keys.TAB)
time.sleep(2)
#也可定位登陆按钮,通过enter(回车)代替click()
driver.find_element_by_name("password").send_keys("123456")
driver.find_element_by_id("login").send_keys(Keys.ENTER)
2、键盘组合键用法:全选和剪切
# 组合键用法:先输入天官赐福进行搜索,然后把输入框内容全选剪切后再次输入雾山五行进行搜索
driver.implicitly_wait(10)
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'a') #全选
time.sleep(2)
driver.find_element_by_id("kw").send_keys(Keys.CONTROL, 'x') #剪切
time.sleep(2)
driver.find_element_by_id("kw").send_keys("雾山五行")
driver.find_element_by_id("su").click()
Web测试中,有关鼠标的操作,不只是单击,有时候还要做右击、双击、拖动等操作。这些操作包含在ActionChains类中。
常用的鼠标方法:
需要引入 ActionChains 类:
from selenium.webdriver.common.action_chains import ActionChains
#coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
import time
driver = webdriver.Chrome()
driver.get("http://news.baidu.com")
qqq =driver.find_element_by_xpath(".//*[@id='s_btn_wr']")
ActionChains(driver).context_click(qqq).perform() #右键
ActionChains(driver).double_click(qqq).perform() #双击
#定位元素的原位置
element = driver.find_element_by_id("s_btn_wr")
#定位元素要移动到的目标位置
target = driver.find_element_by_class_name("btn")
#执行元素的移动操作
ActionChains(driver).drag_and_drop(element, target).perform()
下面开始会用到一些 html 文件进行 API 的辅助理解,文件我放在 github 上,大家可以打开便于理解,在介绍部分功能时我会放部分代码,完整的在这个模块最下面给出。
html文件:https://github.com/Consini/other/tree/master/selenium/html
webdriver 可以很方便的使用findElement 方法来定位某个特定的对象,不过有时候我们却需要定位一组对象,这时候就需要使用findElements 方法。
定位一组对象一般用于以下场景:
get_attribute:获得属性值。
# 选择页面上所有的input,然后从中过滤出所有的 checkbox 并勾选之
inputs = driver.find_elements_by_tag_name('input')
for input in inputs:
if input.get_attribute('type') == 'checkbox':
input.click()
多层框架或窗口的定位:
有时候我们定位一个元素,定位器没有问题,但一直定位不了,这时候就要检查这个元素是否在一个frame 中,seelnium webdriver 提供了一个switch_to_frame 方法,可以很轻松的来解决这个问题。
switch_to_frame(name):用switch_to_frame方法去获取frame中嵌入的页面,对那个页面里的元素进行定位。
switch_to_default_content:从frame中嵌入的页面里跳出,跳回到最外面的原始页面中。
driver.switch_to_window("windowName"):有可能嵌套的不是框架,而是窗口,还有针对窗口的方法
#从默认页面跳转到 ifrome1(id = f1)
driver.switch_to.frame("f1")
#再找到其下面的 ifrome2 (id = f2)
driver.switch_to.frame("f2")
#下面就可以正常的操作元素了
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(2)
# 跳转到默认页面
driver.switch_to.default_content()
driver.switch_to.frame("f1")
driver.find_element_by_link_text("click").click()
定位思路:具体思路是:先点击显示出1个下拉菜单,然后再定位到该下拉菜单所在的ul,再定位这个ul 下的某个具体的link。在这里,我们定位第1个下拉菜单中的Action 这个选项。
#点击Link1链接(弹出下拉列表)
driver.find_element_by_link_text('Link1').click()
driver.implicitly_wait(10)
#方法 1:通过文本链接Action定位
action = driver.find_element_by_link_text('Action')
driver.implicitly_wait(10)
webdriver.ActionChains(driver).move_to_element(action).perform()
#方法 2: 找到id 为dropdown1的父元素
# WebDriverWait(driver,10).until(lambda the_driver:
# the_driver.find_element_by_id('dropdown1').is_displayed())
# #在父亲元件下找到link 为Action 的子元素
# menu = driver.find_element_by_id('dropdown1').find_element_by_link_text('Action')
# #鼠标定位到子元素上
# webdriver.ActionChains(driver).move_to_element(menu).perform()
下拉框是我们最常见的一种页面元素,对于一般的元素,我们只需要一次就定位,但下拉框里的内容需要进行两次定位,先定位到下拉框,再定位到下拉框内里的选项。
现在我们来通过脚本选择下拉列表里的$10.69
# 方法1:进行两次定位
#先定位到下拉框
m=driver.find_element_by_id("ShippingMethod")
#再点击下拉框下的选项
m.find_element_by_xpath("//option[@value='10.69']").click()
# 方法1.2:xpath 定位
driver.find_element_by_xpath("//*[@id='ShippingMethod']/option[3]").click()
# 方法2: 先定位一组元素
lists = driver.find_elements_by_tag_name("option")
for list in lists:#遍历寻找自己想要的
if list.get_attribute('value') == '10.69':
list.click()
lists[2].click() # 通过下标定位
alert.html是一个页面点击按钮后出现警告框,然后打印警告框内容,send.html是警告框中会有一个输入框,在里面输入内容。
注意:switch_to_alert()只能处理原生的alert
alert = driver.switch_to.alert#接受警告信息
time.sleep(1)
print("alert text:"+alert.text)#打印警告信息
alert.accept()#确定
实现的是点击 click 弹出警告框,警告框中还有一个 div,点击 click me 后上面的文本信息会改变。
上传过程一般要打开一个本地窗口,从窗口选择本地文件添加。所以,一般会卡在如何操作本地窗口添加上传文件。只要定位上传按钮,通过send_keys 添加本地文件路径就可以了。绝对路径和相对路径都可以,关键是上传的文件存在。
driver.get(file_path)
driver.find_element_by_name("file").send_keys("D:\\Program Files\\python\\project\\selenium\\html\\upload.html")
关于 html 文件的所有操作:
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
import time
import os
driver = webdriver.Chrome()
driver.maximize_window()
# 定位一组元素,get_attribute:获得属性值
'''
file_path = 'file:///' + os.path.abspath('html\\checkbox.html')
driver.get(file_path)
driver.maximize_window()
# 选择页面上所有的input,然后从中过滤出所有的 checkbox 并勾选之
inputs = driver.find_elements_by_tag_name('input')
for input in inputs:
if input.get_attribute('type') == 'checkbox':
input.click()
'''
# 多层窗口定位:通过switch_to_frame() 方法来进行定位
'''
file_path = 'file:///' + os.path.abspath('html\\frame.html')
driver.get(file_path)
driver.implicitly_wait(30)
#从默认页面跳转到 ifrome1(id = f1)
driver.switch_to.frame("f1")
#再找到其下面的 ifrome2 (id = f2)
driver.switch_to.frame("f2")
#下面就可以正常的操作元素了
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(2)
# 跳转到默认页面
driver.switch_to.default_content()
driver.switch_to.frame("f1")
driver.find_element_by_link_text("click").click()
'''
# 层级定位
'''
file_path = 'file:///' + os.path.abspath('html\\level_locate.html')
driver.get(file_path)
#点击Link1链接(弹出下拉列表)
driver.find_element_by_link_text('Link1').click()
driver.implicitly_wait(10)
#方法 1:通过文本链接Action定位
action = driver.find_element_by_link_text('Action')
driver.implicitly_wait(10)
webdriver.ActionChains(driver).move_to_element(action).perform()
#方法 2: 找到id 为dropdown1的父元素
# WebDriverWait(driver,10).until(lambda the_driver:
# the_driver.find_element_by_id('dropdown1').is_displayed())
# #在父亲元件下找到link 为Action 的子元素
# menu = driver.find_element_by_id('dropdown1').find_element_by_link_text('Action')
# #鼠标定位到子元素上
# webdriver.ActionChains(driver).move_to_element(menu).perform()
'''
#下拉框处理
'''
file_path = 'file:///' + os.path.abspath('html\\drop_down.html')
driver.get(file_path)
# 方法1:xpath 定位
driver.find_element_by_xpath("//*[@id='ShippingMethod']/option[3]").click()
# 方法2: 先定位一组元素
lists = driver.find_elements_by_tag_name("option")
for list in lists:#遍历寻找自己想要的
if list.get_attribute('value') == '10.69':
list.click()
lists[2].click() # 通过下标定位
'''
# alert 框处理:switch_to.alert
# text 返回alert/confirm/prompt 中的文字信息
# accept 点击确认按钮
# dismiss 点击取消按钮,如果有的话
'''
file_path = 'file:///' + os.path.abspath('html\\alert.html')
driver.get(file_path)
driver.find_element_by_id("tooltip").click()# 在这个例子中 id,link text 都可以用来定位
time.sleep(1)
alert = driver.switch_to.alert
time.sleep(1)
print("alert text:"+alert.text)
alert.accept()
'''
# send_keys 输入值,这个alert\confirm 没有对话框就不能用了,不然会报错
'''
file_path = 'file:///' + os.path.abspath('html\\send.html')
driver.get(file_path)
#driver.find_element_by_xpath("/html/body/input").click()
driver.find_element_by_tag_name("input").click()
alert = driver.switch_to.alert
alert.send_keys("少卿大人")
alert.accept()
'''
# div 模块的处理
'''
file_path = 'file:///' + os.path.abspath('html\\modal.html')
driver.get(file_path)
time.sleep(3)
driver.find_element_by_id("show_modal").click()
time.sleep(1)
ddiv = driver.find_element_by_class_name("modal-body")
ddiv.find_element_by_id("click").click()
#ddiv.find_element_by_partial_link_text("click").click()
time.sleep(1)
#button = driver.find_element_by_class_name("modal-footer").find_elements_by_tag_name("button")
buttons = driver.find_elements_by_tag_name("button")
buttons[0].click()
'''
# 上传文件操作
file_path = 'file:///' + os.path.abspath('html\\upload.html')
driver.get(file_path)
driver.find_element_by_name("file").send_keys("D:\\Program Files\\python\\project\\selenium\\html\\upload.html")
time.sleep(2)
driver.quit()
unittest 单元测试提供了创建测试用例,测试套件以及批量执行的方案, unittest 在安装pyhton 以后就直接自带了,直接import unittest 就可以使用。
作为单元测试的框架, unittest 也是可以对程序最小模块的一种敏捷化的测试。在自动化测试中,我们虽然不需要做白盒测试,但是必须需要知道所使用语言的单元测试框架。利用单元测试框架,创建一个类,该类继承unittest的TestCase,这样可以把每个case看成是一个最小的单元, 由测试容器组织起来,到时候直接执行,同时引入测试报告。
setUp():进行初始化,定义全局变量:定义方法 : 类的实例.变量:self.driver
tearDown():进行清理工作,
可以增加verbosity参数,例如unittest.main(verbosity=2)
在主函数中,直接调用main() ,在main中加入verbosity=2 ,这样测试的结果就会显示的更加详细。
这里的verbosity 是一个选项, 表示测试结果的信息复杂度,有三个值:
0 ( 静默模式): 你只能获得总的测试用例数和总的结果比如总共100个失败,20 成功80
1 ( 默认模式): 非常类似静默模式只是在每个成功的用例前面有个“ . ” 每个失败的用例前面有个“F”
2 ( 详细模式): 测试结果会显示每个测试用例的所有相关的信息
1、构建测试套件
完整的单元测试很少只执行一个测试用例,开发人员通常都需要编写多个测试用例才能对某一软件功能进行比较完整的测试,这些相关的测试用例称为一个测试用例集,在unittest中是用TestSuite 类来表示的。假设我们已经编写了testbaidu1.pytestbaidu2.py两个文件,那么我们怎么同时执行这两个文件呢?
testbaidu1.py:https://github.com/Consini/other/blob/master/selenium/testbaidu1.py
testbaidu2.py:https://github.com/Consini/other/blob/master/selenium/testbaidu2.py
(1)addTest() 的应用
当有多个或者几百测试用例的时候, 这样就需要一个测试容器( 测试套件) ,把测试用例放在该容器中进行执行,unittest 模块中提供了TestSuite 类来生成测试套件,使用该类的构造函数可以生成一个测试套件的实例,该类提供了addTest来把每个测试用例加入到测试套件中。
缺点:
(2)makeSuite()和TestLoader()的应用
在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。
TestLoader 用于创建类和模块的测试套件,一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#导入testbaidu1,testbaidu2
import testbaidu1
import testbaidu2
#手工添加案例到套件,
def createsuite():
suite = unittest.TestSuite()
# 将某个类的某个测试用例加入到测试容器(套件)中
# suite.addTest(testbaidu1.Baidu1("test_baidusearch"))
# suite.addTest(testbaidu1.Baidu1("test_hao"))
# suite.addTest(testbaidu2.Baidu2("test_baidusearch"))
# 把一个类里面的所有测试方法添加到套件里面,makeSuite可以实现把测试用例类内所有的测试case组成的测试套
# 件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可
# suite.addTest(unittest.makeSuite(testbaidu1.Baidu1))
# suite.addTest(unittest.makeSuite(testbaidu2.Baidu2))
# 把一个类里面的所有测试方法添加到套件里面,TestLoader 用于创建类和模块的测试套件
suite1 = unittest.TestLoader().loadTestsFromTestCase(testbaidu1.Baidu1)
suite2 = unittest.TestLoader().loadTestsFromTestCase(testbaidu2.Baidu2)
suite = unittest.TestSuite([suite1, suite2])
return suite
if __name__=="__main__":
suite = createsuite()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
经过makeSuite()和TestLoader()的引入,我们不用一个py文件测试类,只需要导入一次即可。那么能不能测试类也不用每次添加指定呢?
(3)discover()的应用
discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为discover(dir,pattern,top_level_dir=None)
# -*- coding: utf-8 -*-
import unittest,csv
import os,sys
import time
#手工添加案例到套件
def createsuite():
discover = unittest.defaultTestLoader.discover('../selenium', pattern='test*.py', top_level_dir=None)
print(discover)
return discover
if __name__=="__main__":
suite = createsuite()
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite)
2、用例的执行顺序
方法默认执行需要以 test_ 开头
unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0~9,A~Z,a~z 。
addTest()方法按照增加顺序来执行。
3、忽略用例执行
@unittest.skip("skipping")
自动化的测试中, 对于每个单独的case来说,一个case的执行结果中, 必然会有期望结果与实际结果, 来判断该case是通过还是失败, 在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果, 一般来说, 检查条件大体分为等价性, 逻辑比较以及其他, 如果给定的断言通过, 测试会继续执行到下一行的代码, 如果断言失败, 对应的case测试会立即停止或者生成错误信息( 一般打印错误信息即可) ,但是不要影响其他的case执行。
unittest 的单元测试库提供了标准的xUnit 断言方法。下面是一些常用的断言:
#断言,断言失败后下面的内容不会继续运行
#判断打开的网址是否是百度
self.assertEqual(driver.title,"百度一下",msg="Not Equal")#断言相等,结果为false,输出msg
self.assertEqual(driver.title,"百度一下,你就知道",msg="Not Equal")#断言相等,结果为true,不影响什么
self.assertNotEqual(driver.title,"百度一下",msg="233333") #断言不相等,结果为true,运行成功
self.assertNotEqual(driver.title,"百度一下,你就知道",msg="233333") # 断言不相等,结果为false输出msg
self.assertTrue("123"=="1234",msg="不相等")#断言123等于1234,结果为false,输出:不相等
可以通过 IDE 来断言:
脚本执行完毕之后,还需要看到HTML报告,下面我们就通过HTMLTestRunner.py 来生成测试报告。
HTMLTestRunner支持python2.7。python3可以参见https://blog.51cto.com/hzqldjb/1590802来进行修改。
HTMLTestRunner.py 文件,下载地址: http://tungwaiyip.info/software/HTMLTestRunner.html
下载后将其放在testcase目录中去或者放入...\Python27\Lib 目录下(windows)。
# -*- coding: utf-8 -*-
import unittest
import os,sys
import time
import HTMLTestRunner
#手工添加案例到套件,
def createsuite():
discover=unittest.defaultTestLoader.discover('../selenium',pattern='testba*.py',top_level_dir=None)
print(discover)
return discover
if __name__=="__main__":
# #sys.path 是python的搜索模块的路径集,返回结果是一个list
# curpath=sys.path[0]
#
# print(sys.path)
# print("************")
# print(sys.path[0])
#
# #取当前时间
# '''
# strftime作用是格式化时间戳为本地时间
# time()函数用于返回当前时间的时间戳(1970年01月08时00分00秒到现在的浮点秒数)
# localtime()函数作用是格式化时间戳为本地 struct_time
# '''
# if not os.path.exists(curpath+'/resultreport'):
# os.makedirs(curpath+'/resultreport')
# ./ 表示当前目录
if not os.path.exists('./resultreport'):
os.mkdir('./resultreport')
now=time.strftime("%Y-%m-%d-%H %M %S",time.localtime(time.time()))
# filename = curpath + '/resultreport/' + now + 'resultreport.html'
filename = './resultreport/' + now + 'resultreport.html'
with open(filename, 'wb') as fp:
# 出html报告
runner = HTMLTestRunner.HTMLTestRunner(stream=fp, title=u'测试报告', description=u'用例执行情况', verbosity=2)
suite = createsuite()
runner.run(suite)
脚本运行出现一些异常,但又不想将异常直接展示给用户,或者不想因为异常导致脚本不能继续运行下去,这时就需要 try……except……finally来捕获异常,捕捉之后需要进行记录,这个时候就用到错误截图 驱动.get_screenshot_as_file(filename)。
用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。
例如编写一个函数,关键语句为driver.get_screenshot_as_file:
def savescreenshot(self, driver, file_name):
if not os.path.exists('./errorImage'):
os.makedirs('./errorImage')
now = time.strftime("%Y%m%d-%H%M%S", time.localtime(time.time()))
# 截图保存
driver.get_screenshot_as_file('./errorImage/' + now + '-' + file_name)
time.sleep(1)
一个引用的例子:https://github.com/Consini/other/blob/master/selenium/testscreenshot.py
之前我们的case都是数据和代码在一起编写。考虑如下场景:
需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这时候需要编写3个案例吗?有没有版本一次运行?
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成。
1、ddt 的安装
pip install ddt//安装
pip show sst//检查安装是否成功
2、ddt 使用方法:参考文档:http://ddt.readthedocs.io/en/latest/
3、from ddt import ddt, data, unpack ,file_data
testddt.py:https://github.com/Consini/other/blob/master/selenium/testddt.py
https://github.com/Consini/other/tree/master/selenium
文件目录解释: