自动化前置知识
什么是自动化测试
在预设状态下运行应用程序或者系统,预设条件包括正常和异常,最后评估运行结
果。将人为驱动的测试行为转化为机器(电脑)执行的过程。
自动化测试工具
jmeter:开源免费 性能测试,接口测试, 基于Java的压力测试工具
appium: 开源免费 手机APP自动化测试工具
selenium: 开源免 web的 UI自动化测试
自动化测试分类
- 从软件生命周期或分层角度分类:
单元自动化测试:
单元自动化测试是指自动化地完成对代码中的类或方法进行测试,主要关注代码的实现细节以及业务逻辑等方面。
- 接口自动化测试
接口自动化测试用于测试系统组件件接口的请求与返回。接口测试稳定性高,更适合开展自动化测试。
前提:
- 项目前期就可以介入
- 测试用例维护量少
- 接口稳定
- UI自动化测试
用自动化技术对图形界面进行流程和功能方面验证的过程。
前提/场景:
- 项目比较稳定
- 界面要稳定
- 项目后期做UI自动化测试
- 用例维护量大
- 从测试目的角度分类
- 功能自动化
功能自动化测试主要检查实际功能是否符合用户的需求,主要以回归测试为主,涉及图形界面,数据库连接,以及其他比较稳定而不经常发送变化的元素
- 性能自动化
性能自动化测试是托管自动化平台的执行性能测试,收集测试结果,并能分析测试结果的一种可以接近无人值守的性能测试。
自动化测试的优势
- 更方便对系统进行回归测试,当软件的版本发布比较频繁时,自动化测试的效率更加明显
- 可以自动处理原本繁琐,重复的任务,提高测试的准确性和测试人员的积极性
- 自动化测试具有复用性和一致性,即测试脚本可以在不同的版本上重复运行,且可以保障猜测内容的一致性
自动化测试的适用条件/对象
页面稳定,需求变动不频繁,项目周期长,交互性弱
自动化测试的不适用条件/对象
- 需求变动频繁的项目,自动化脚本不能重复使用,维护成本太大,性价比低
- 项目周期短,自动化脚本编制完成后使用次数不多,性价比低
- 交互型较强的项目,需要人工干预的项目,自动化无法实施
如何实施自动化测试
- 分析:总体把握系统逻辑,分析出系统的核心体系架构。
- 设计:设计测试用例,测试用例要足够明确和清晰,覆盖面广而精
- 实现:实现脚本,有两个要求一是断言,二是合理的运用参数化。
- 执行:执行脚本远远没有我们想象中那么简单。脚本执行过程中的异常需要我们仔细的去分析原因。
- 总结:测试结果的分析,和测试过程的总结是自动化测试的关键。
- 维护:自动化测试脚本的维护是一个难以解决但又必须要解决的问题。
- 分析:在自动化测试过程中深刻的分析自动化用例的覆盖风险和脚本维护的成本。
自动化测试的失败因素
- 期望值过高。就像管理人员要求完全测试一样,期望100%的测试自动化,也同样是一个不现实的需求。
- 对收益和成本认识不清。抛开工具的开发或者购买成本和培训成本,自动化测试的成本应该还包括两部分(实现
成本中还隐含了测试准备成本):
成本=实现成本+运行维护成本
自动化测试的收益是由测试脚本的重复运行次数,或自动测试脚本的利用率决定的。
自动化测试的发展方向
- 自动化脚本的执行,维护和结果分析
- 自动化脚本的编写
- 自动化测试方案的设计,框架的选型
- 自动化测试框架的编码实现
- 自动化测试框架的设计
自动化测试需要的技能
- 了解基本业务
- 了解业务的技术框架
- 懂得功能测试
- 懂得一种编程语言
- 懂数据库、操作系统
- 了解常见的测试框架
selenium
什么是selenium
一套完整的web应用程序测试系统,开源Web功能测试工具系列(工具集):selenium1.0 和selenium2.0
包含测试的录制(selenium IDE),编写和运行(selenium Remote Control)和测试的并行处理(selenium Grid)
selenium的核心是selenium Core基于JsUnit,完全由JavaScript编写因此可以运行任何支持JavaScript的浏览器,它由一种指定格式的HTML的文件驱动在一定程度上增强了测试套件(Test Suite)的可读性
**selenium IDE:**自动化脚本录制工具
**selenium Remote Control:**允许测试人员使用常见的语言编写测试代码,并支持不同的操作系统下的各种主流浏览器
**selenium Grid:**将测试分发至多台机器,这样便可大大加快测试速度
selenium Remote Control原理
selenium RC主要由两部分组成:selenium Server+Client Libraries。其中Selenium Server负责控制浏览器的行为。而Client Libraries则是给测试人员编写测试案例时用来控制selenium Server的库。
selenium1的自动化执行步骤如下:
- 测试人员基于Selenium支持的编程语言编写好测试脚本程序。
- 测试人员执行测试程序。
- 测试脚本程序发送访问网站的HTTP请求给Remote Control Server(RC)。
- RC 收到请求后,访问被测试网站并获取网页数据内容,并在网页中插入Selenium Core的JavaScript代码库,然后返回给测试人员执行测试的浏览器。
- 测试脚本在浏览器内部再调用selenium Core来执行测试代码逻辑,记录测试结果,完成测试。
selenium1的代理模式
selenium1代理模式的实现机制如下:
- 执行测试脚本,脚本向Selenium Server发起请求,要求和Selenium Server建立链接。
- Selenium Server的Launcher启动浏览器,向浏览器中插入Selenium Core的JavaScript代码库,并把浏览器的代理设置为Selenium Server的Http Proxy,确保后续Core的脚本域被访问的网站的脚本同源。
- 测试脚本向seleniumServer发送Http请求,Selenium Server对请求进行解析,然后通过Http Proxy发送JS命令通知Selenium Core发送JS命令通知Seleniim Core执行操作浏览器的请求。
- Selenium Core收到指令后,执行测试脚本里指定的网页操作命令。
- 浏览器收到新的请求信息,于是发送Http请求给Selenium给Selenium Server里的Http Proxy,请求新的Web页面。(因为第二步中,selenium Server在启动浏览器的时候,已经把浏览器的代理地址设定为Selenium Server的Http Proxy)
- Selenium Server接收到请求后,自行重组http请求,向应用服务器发送请求并获取返回的web页面。
- Selenium Server的Http Proxy把接收到的Web页面返回给浏览器。
selenium2.0
**组成:**selenium1.0+webDriver
webdriver
定义: 由于客户端脚本(java,python,ruby)不能直接和浏览器通信,这时候可以把webservice当做一个翻译器,它可以把客户端代码翻译成浏览器可以识别的代码(比如js),客户端(也就是测试脚本)创建一个session,在该session中通过http请求向webservice发送restful的请求,webservice翻译成浏览器懂得脚本传给浏览器,浏览器把执行的结果返回给webservice,webservice把返回的结果做了一些封装(一般都是json格式),然后返回给client,根据返回值就能判断对浏览器的操作是不是执行成功。
python+selenium安装
- 安装python+pycharm
安装python+pycharm
安装setuptools高版本自带
安装selenium:
pip install selenium -i https://mirrors.aliyun.com/pypi/simple
- 安装浏览器驱动
驱动镜像地址: https://npm.taobao.org/mirrors/chromedriver/
- 查看浏览器版本
- 下载对应版本的驱动
- 解压,将chromedriver.exe放置在对应的python路径下
webdriver使用
元素定位
id属性
driver.find_element_by_id("id值")
#导包
from selenium import webdriver
import time
#对应浏览器驱动实例
driver=webdriver.Chrome()
#浏览器加载url
driver.get("https://ww.baidu.com/")
#根据百度搜索框的id定位它,并输入selenium
driver.find_element_by_id("kw").send_keys("selenium")
#等待4秒
time.sleep(4)
#关闭浏览器
driver.quit()
name属性
driver.find_element_by_name("name值")
class属性
driver.find_element_by_class_name("class值")
target name(标签名)
driver.find_element_by_tag_name("标签名")
link text(链接内容)
driver.find_element_by_link_text("链接内容")
partial link text(部分链接内容)
driver.find_element_by_partial_link_text("链接部分内容")
xpath
XPath 是一种在XML 文档中定位元素的语言。因为HTML 可以看做XML 的一种实现,所以selenium 用户可是使用这种强大语言在web 应用中定位元素。
获取方式:
网页->检查->鼠标右击标签->点击copy->copy xpath
driver.find_element_by_xpath("xpath路径")
css selector(css选择器)
网页->检查->鼠标右击标签->点击copy->copy selector
driver.find_element_by_css_selector("css选择器")
#id选择器
driver.find_element_by_css_selector("#id属性")
#class选择器
driver.find_element_by_css_selector(".class属性")
#标签选择器
driver.find_element_by_css_selector("标签名")
元素定位实例
from selenium import webdriver
import time
#对应浏览器驱动
driver=webdriver.Chrome()
#浏览器加载url
driver.get("https://ww.baidu.com/")
#根据百度搜索框的id定位它,并输入selenium
#driver.find_element_by_id("kw").send_keys("selenium")
#根据百度搜索框的name属性定位它,并输入selenium
#driver.find_element_by_name("wd").send_keys("selenium")
#根据百度搜索框的class属性定位
#driver.find_element_by_class_name("s_ipt").send_keys("selenium")
#根据标签名定位
#driver.find_element_by_tag_name("input").send_keys("selenium")
#链接
#driver.find_element_by_link_text("hao123").click()
#部分链接
#driver.find_element_by_partial_link_text("123").click()
#xpath
#driver.find_element_by_xpath("//*[@id='kw']").send_keys("selenium")
#id选择器
driver.find_element_by_xpath("#kw").send_keys("selenium")
#class选择器
driver.find_element_by_css_selector(".s_ipt").send_keys("selenium")
#等待4秒
time.sleep(4)
#关闭浏览器
driver.quit()
操作测试对象
click()
用于点击一个按钮
#定位百度一下按钮,点击按钮
driver.find_element_by_id("su").click()
send_keys()
用于在一个输入框里输入xx 内容
#定位百度搜索框,输入内容
driver.find_element_by_id("kw").send_keys("selenium")
clear()
用于清除输入框的内容
#定位百度搜索框,清除搜索框内容
driver.find_element_by_id("kw").clear()
submit()
提交表单
#定位百度搜索框,提交表单
driver.find_element_by_id("kw").submit()
text
用于获取元素的文本信息
#定位百度首页的hao123链接,获取连接内容
data=driver.find_element_by_link_text("hao123").text
#打印内容
print(data)
添加等待
固定等待:sleep()
import time time.sleep(2)//等待2秒
智能等待:implicitly_wait()
driver.implicitly_wait(10)//智能等待10秒
对比固定等待和智能等待
固定等待:
设置固定休眠时间,单位为秒。 由python的
time
包提供, 导入 time 包后就可以使用。缺点:
不智能,使用太多的sleep会影响脚本运行速度。智能等待:/隐式等待
由webdriver提供的方法,一旦设置,这个隐式等待会在WebDriver对象实例的整个生命周期起作用,它不针对某一个元素,是全局元素等待,即在定位元素时,需要等待页面全部元素加载完成,才会执行下一个语句
title和url
title
print(driver.title)
url
print(driver.current_url)
浏览器的操作
浏览器的最大化
driver.maximize_window()
浏览器的最小化
driver.minimize_window()
设置浏览器的宽高
driver.set_window_size(480,800)
操作浏览器的前进和后退
back():后退
driver.back()
forward():前进
driver.forward()
实例
from selenium import webdriver import time driver=webdriver.Chrome() #访问百度首页 driver.get("https://ww.baidu.com/") time.sleep(2) #访问新闻首页 driver.get("http://news.baidu.com") time.sleep(2) #后退 driver.back() time.sleep(2) #前进 driver.forward() time.sleep(2) driver.quit()
控制浏览器的滚动条
将滚动条拖动至页面的底部
js="var q=document.documentElement.scrollTop=10000" driver.execute_script(js)
将滚动条拖动至页面的顶部
js="var q=document.documentElement.scrollTop=0" driver.execute_script(js)
实例
from selenium import webdriver import time driver=webdriver.Chrome() driver.get("https://ww.baidu.com/") driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() time.sleep(3) js="var q=document.documentElement.scrollTop=10000" driver.execute_script(js) time.sleep(3) js="var q=document.documentElement.scrollTop=0" driver.execute_script(js) driver.quit()
键盘事件
键盘按键
导包
from selenium.webdriver.common.keys import Keys
Keys.TAB
#禅道用户名框TAB切换到密码框 driver.find_element_by_id("account").send_keys(Keys.TAB)
Keys.ENTER
#禅道密码框按enter登录 driver.find_element_by_name("password").send_keys(Keys.ENTER)
实例
from selenium import webdriver import time from selenium.webdriver.common.keys import Keys #需要引入keys 包 driver=webdriver.Chrome() driver.get("http://127.0.0.1:81/zentao/user-login-L3plbnRhby8=.html") driver.find_element_by_id("account").send_keys("admin") time.sleep(2) #禅道用户名框TAB切换到密码框 driver.find_element_by_id("account").send_keys(Keys.TAB) time.sleep(2) driver.find_element_by_name("password").send_keys("zpf20000211ZPF") time.sleep(2) #点击登录按钮登录 #driver.find_element_by_id("submit").click() #密码框按enter登录 driver.find_element_by_name("password").send_keys(Keys.ENTER) time.sleep(2) driver.quit()
键盘组合键
Ctrl+a
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'a')
Ctrl+x
driver.find_element_by_id("kw").send_keys(Keys.CONTROL,'x')
实例
from selenium import webdriver from selenium.webdriver.common.keys import Keys import time driver=webdriver.Chrome() driver.get("https://ww.baidu.com/") driver.find_element_by_id("kw").send_keys("selenium") time.sleep(2) #百度输入框全选内容 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.quit()
鼠标事件
ActionChains()
生成用户的行为。所有的行动都存储在actionchains 对象
ActionChains(driver)
perform()
执行所有存储的行为
context_click() 右击
c.context_click(su).perform()
double_click() 双击
ActionChains(driver).double_click(su).perform()
drag_and_drop() 拖动
ActionChains(driver).drag_and_drop(su,link).perform()
move_to_element() 移动
ActionChains(driver).move_to_element(link).perform()
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
import time
driver=webdriver.Chrome()
driver.get("https://ww.baidu.com/")
#driver.find_element_by_id("kw").send_keys("selenium")
time.sleep(3)
su=driver.find_element_by_id("su")
# time.sleep(3)
#右键
# ActionChains(driver).context_click(su).perform()
# time.sleep(3)
#双击
# ActionChains(driver).double_click(su).perform()
# time.sleep(3)
#移动焦点到新闻链接上
link=driver.find_element_by_link_text("新闻")
#ActionChains(driver).move_to_element(link).perform()
ActionChains(driver).drag_and_drop(su,link).perform()
time.sleep(3)
driver.quit()
定位一组元素
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/checkbox.html" driver.get(url) #获取input集合 inputs=driver.find_elements_by_tag_name("input") print(type(inputs)) #遍历集合,找checkbox并勾选 for input in inputs: if input.get_attribute('type')=='checkbox': input.click() time.sleep(3) driver.quit()
多层框架/窗口
switch_to.frame()
driver.switch_to.frame("f1")
switch_to.window()
多层窗口定位
switch_to.default_content()
实例
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/frame.html" driver.get(url) #定位 iframe (id=f1)外框架 driver.switch_to.frame("f1") #定位iframe(id=f2)内框架 driver.switch_to.frame("f2") driver.find_element_by_id("kw").send_keys("selenium") time.sleep(2) driver.find_element_by_id("su").click() time.sleep(2) #再回到默认页面 driver.switch_to.default_content() #定位f1 driver.switch_to.frame("f1") #定位click按钮 driver.find_element_by_link_text("click").click() time.sleep(2) driver.quit()
层级定位
from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time driver=webdriver.Chrome() #加载url url="file:///E:/pythonProject/test/selenium2html/level_locate.html" driver.get(url) #定位链接link1 driver.find_element_by_link_text("Link1").click() time.sleep(2) #定位下拉菜单的元素 dropdown=driver.find_element_by_id("dropdown1").find_element_by_link_text("Another action") time.sleep(2) #将鼠标移动到指定位置 ActionChains(driver).move_to_element(dropdown).perform() time.sleep(2) driver.quit()
下拉框处理
#疑惑:怎么把鼠标移动到select框中的option上 from selenium import webdriver from selenium.webdriver.common.action_chains import ActionChains import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/drop_down.html" driver.get(url) time.sleep(2) #定位下拉框,并点击显示 select=driver.find_element_by_id("ShippingMethod") select.click() time.sleep(2) #定位选择下拉框的元素 option=select.find_element_by_xpath("//*[@id='ShippingMethod']/option[2]") option.click() #options[1].click() #time.sleep(3) #select.click() #ActionChains(driver).double_click(select).perform() #无法实现将鼠标移动到option #ActionChains(driver).move_to_element(option).perform() #ActionChains(driver).drag_and_drop(select,option).perform() time.sleep(3) driver.quit()
弹出框处理
- text 返回alert/confifirm/prompt 中的文字信息
- accept 点击确认按钮
- dismiss 点击取消按钮,如果有的话
- send_keys 输入值,这个alert\confifirm 没有对话框就不能用了,不然会报错
alert()
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/alert.html" driver.get(url) time.sleep(2) driver.find_element_by_id("tooltip").click() time.sleep(2) #获取alert中的内容 content=driver.switch_to.alert.text print(content) time.sleep(2) #点击确认按钮关闭alert driver.switch_to.alert.accept() time.sleep(2) driver.quit()
confirm()
prompt()
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/send.html" driver.get(url) driver.find_element_by_tag_name("input").click() time.sleep(2) pro=driver.switch_to.alert pro.send_keys("fpz") time.sleep(2) # content=pro.text # print(content) pro.accept() time.sleep(2) # pro.dismiss() # time.sleep(2) driver.quit()
div对话框
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/modal.html" driver.get(url) time.sleep(2) #打开对话框 driver.find_element_by_id("show_modal").click() time.sleep(2) #点击对话框中的链接 driver.find_element_by_id("click").click() time.sleep(2) #关闭对话框 #多个元素默认获取获取第一个元素 button=driver.find_element_by_class_name("modal-footer").find_element_by_tag_name("button") button.click() time.sleep(2) driver.quit()
上传文件
from selenium import webdriver import time driver=webdriver.Chrome() url="file:///E:/pythonProject/test/selenium2html/upload.html" driver.get(url) time.sleep(2) #定位上传按钮,上传文件 driver.find_element_by_name("file").send_keys("E:/TuPian/out.jpg") time.sleep(2) driver.quit()
自动化测试框架
unittest简介
- unittest是python的一单元测试框架,常见的还有:pytest
- Junit和TestNG是java的单元测试框架
组件
- TestCase测试用例类,一个TestCase实例就是一个测试用例,
什么是自动化测试用例
完整的测试流程,包括准备环境的搭建(SetUp),执行测试(run),测试后环境的清理
- TestFixture(测试固件)
表示为了一项或多项测试所需要进行的准备工作,以及相关的清理操作。
通俗的说就是对一个测试动力环境的搭建和销毁,通过重写TestCase的setUp()和tearDown()方法来实现,
**好处:**为以后的TestCase留下一个干净的环境
**举例:**比如说这个测试用例需要访问数据库,那么可以在setUp()中建立数据库连接以及进行一些初始化,在tearDown()中清除在数据库中产生的数据,然后关闭连接。
- TestSuite(测试套件)
一系列的测试用例,或测试套件,或两者皆有,用于归档需要一起执行的测试用例
通俗说就是多个测试用例的集合就是Test Suite,Test Suite也可以嵌套Test Suite
- TestRunner(测试运行器)
是一个用于执行和输出测试结果的组件,这个运行器可能使用图形接口,文本接口,或返回一个待定的值表示运行测试的结果
- TestReport
生成测试报告
测试用例TestCase
import time
import unittest
from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException
class testCase1(unittest.TestCase):
#初始化环境
def setUp(self):
self.driver=webdriver.Chrome()
self.driver.get("https://ww.baidu.com/")
self.driver.maximize_window()
time.sleep(3)
#测试用例test_开头会默认自动执行
def test_baidu1(self):
driver=self.driver
driver.find_element_by_id("kw").send_keys("selenium")
driver.find_element_by_id("su").click()
time.sleep(3)
def test_baidu2(self):
driver=self.driver
driver.find_element_by_link_text("新闻").click()
time.sleep(3)
#普通方法
def is_alert_exists(self):
try:
self.driver.switch_to.alert
except NoAlertPresentException as e:
return False
return True
#清除环境
def tearDown(self):
time.sleep(3)
self.driver.quit()
#主函数
if __name__=="__main__":
unittest.main(verbosity=2)
测试套件TestSuite
addTest()
当有多个或者几百测试用例的时候, 这样就需要一个测试容器( 测试套件) ,把测试用例放在该容器中进行执行,unittest 模块中提供了TestSuite 类来生成测试套件,使用该类的构造函数可以生成一个测试套件的实例,该类提供了addTest来把每个测试用例加入到测试套件中。
test001
import time import unittest from selenium import webdriver from selenium.common.exceptions import NoAlertPresentException class testCase1(unittest.TestCase): #初始化环境 def setUp(self): self.driver=webdriver.Chrome() self.driver.get("https://ww.baidu.com/") self.driver.maximize_window() time.sleep(3) #测试用例test_开头会默认自动执行 def test_baidu1(self): driver=self.driver driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() time.sleep(3) def test_baidu2(self): driver=self.driver driver.find_element_by_link_text("新闻").click() time.sleep(3) #普通方法 def is_alert_exists(self): try: self.driver.switch_to.alert except NoAlertPresentException as e: return False return True #清除环境 def tearDown(self): time.sleep(3) self.driver.quit() #主函数 if __name__=="__main__": unittest.main(verbosity=2)
test002
import time import unittest from selenium import webdriver from selenium.common.exceptions import NoAlertPresentException class testCase2(unittest.TestCase): #初始化环境 def setUp(self): self.driver=webdriver.Chrome() self.driver.get("https://ww.baidu.com/") self.driver.maximize_window() time.sleep(3) #测试用例test_开头会默认自动执行 def test_baidu1(self): driver=self.driver driver.find_element_by_id("kw").send_keys("unittest") driver.find_element_by_id("su").click() time.sleep(3) def test_baidu2(self): driver=self.driver driver.find_element_by_link_text("hao123").click() time.sleep(3) #普通方法 def is_alert_exists(self): try: self.driver.switch_to.alert except NoAlertPresentException as e: return False return True #清除环境 def tearDown(self): time.sleep(3) self.driver.quit() #主函数 if __name__=="__main__": unittest.main(verbosity=2)
testSuite
import unittest #纯数字的包名导不进去 from src20210510 import test001 from src20210510 import test002 def createSuite(): #测试套件实例化 suite=unittest.TestSuite() #测试用例加入测试套件 suite.addTest(test001.testCase1("test_baidu1")) suite.addTest(test001.testCase1("test_baidu2")) suite.addTest(test002.testCase2("test_baidu1")) suite.addTest(test002.testCase2("test_baidu2")) return suite if __name__=="__main__": suite=createSuite() runner=unittest.TextTestRunner(verbosity=2) runner.run(suite)
makeSuite()
在unittest 框架中提供了makeSuite() 的方法,makeSuite可以实现把测试用例类内所有的测试case组成的测试套件TestSuite ,unittest 调用makeSuite的时候,只需要把测试类名称传入即可。
import unittest #纯数字的包名导不进去 from src20210510 import test001 from src20210510 import test002 def createSuite(): #测试套件实例化a suite=unittest.TestSuite() suite.addTest(unittest.makeSuite(test001.testCase1)) suite.addTest(unittest.makeSuite(test002.testCase2)) return suite if __name__=="__main__": suite=createSuite() runner=unittest.TextTestRunner(verbosity=2) runner.run(suite)
TestLoader
TestLoader 用于创建类和模块的测试套件,一般的情况下,使TestLoader().loadTestsFromTestCase(TestClass)来加载测试类。
import unittest #纯数字的包名导不进去 from src20210510 import test001 from src20210510 import test002 def createSuite(): #根据给定的测试类,获取其中所有以“test”开头的测试方法,并返回一个测试套件 suite1=unittest.TestLoader().loadTestsFromTestCase(test001.testCase1) suite2=unittest.TestLoader().loadTestsFromTestCase(test002.testCase2) # 将多个测试类加载到测试套件中 #通过调整suit2和suite1的顺序,可以设定执行顺序 suite = unittest.TestSuite([suite1, suite2]) return suite if __name__=="__main__": suite=createSuite() runner=unittest.TextTestRunner(verbosity=2) runner.run(suite)
discover()
discover 是通过递归的方式到其子目录中从指定的目录开始, 找到所有测试模块并返回一个包含它们对象的
TestSuite ,然后进行加载与模式匹配唯一的测试文件,discover 参数分别为
discover(dir,pattern,top_level_dir=None)
import unittest #纯数字的包名导不进去 from src20210510 import test001 from src20210510 import test002 def createSuite(): discover=unittest.defaultTestLoader.discover('../src20210510',pattern='test00*.py',top_level_dir=None) return discover if __name__=="__main__": suite=createSuite() runner=unittest.TextTestRunner(verbosity=2) runner.run(suite)
用例的执行顺序
- unittest 框架默认加载测试用例的顺序是根据ASCII 码的顺序,数字与字母的顺序为: 0-9;A-Z,a-z 。所以, TestAdd 类会优先于TestBdd 类被发现, test_aaa() 方法会优先于test_ccc() 被执行。对于测试目录与测试文件来说, unittest 框架同样是按照这个规则来加载测试用例。
- addTest()方法按照增加顺序来执行
忽略用例的执行
在方法前加上@unittest.skip(“skipping”)
断言
自动化的测试中, 对于每个单独的case来说,一个case的执行结果中, 必然会有期望结果与实际结果, 来判断该
case是通过还是失败, 在unittest 的库中提供了大量的实用方法来检查预期值与实际值, 来验证case的结果, 一
般来说, 检查条件大体分为等价性, 逻辑比较以及其他, 如果给定的断言通过, 测试会继续执行到下一行的代
码, 如果断言失败, 对应的case测试会立即停止或者生成错误信息( 一般打印错误信息即可) ,但是不要影响其他
的case执行。
unittest 的单元测试库提供了标准的xUnit 断言方法。下面是一些常用的断言
import time import unittest from selenium import webdriver from selenium.common.exceptions import NoAlertPresentException class testCase1(unittest.TestCase): #初始化环境 def setUp(self): self.driver=webdriver.Chrome() self.driver.get("https://ww.baidu.com/") self.driver.maximize_window() time.sleep(3) #测试用例test开头会默认自动执行 def test_baidu1(self): driver=self.driver driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() time.sleep(3) #断言 self.assertEqual(driver.title,"百度搜索",msg="not equals") #忽略测试用例 @unittest.skip("skipping") def test_baidu2(self): driver=self.driver driver.find_element_by_link_text("新闻").click() time.sleep(3) #普通方法 def is_alert_exists(self): try: self.driver.switch_to.alert except NoAlertPresentException as e: return False return True #清除环境 def tearDown(self): time.sleep(3) self.driver.quit() #主函数 if __name__=="__main__": unittest.main(verbosity=2)
HTML报告生成
下载一个HTMLTestRunner.py放在python的安装目录下
import HTMLTestRunner import os import sys import time import unittest def createSuite(): discover=unittest.defaultTestLoader.discover('../src20210510',pattern='test00*.py',top_level_dir=None) return discover if __name__=="__main__": # 获取当前文件所在的文件路径 curpath = sys.path[0] print(curpath) #创建文件 if not os.path.exists("/resultReport"): os.makedirs(curpath + "/resultReport") now = time.strftime("%Y-%m-%d-%H %M %S", 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)
异常捕捉和错误截图
用例不可能每一次运行都成功,肯定运行时候有不成功的时候。如果可以捕捉到错误,并且把错误截图保存,这将是一个非常棒的功能,也会给我们错误定位带来方便。
def test_baidu3(self): driver = self.driver #判断打开的是不是百度页面 try: self.assertEqual(driver.title,"百度,你就知道",msg="判断失败,没有打开百度搜索页面") except: self.save_error_image(driver,"baidu.png") def save_error_image(self, driver, 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 + "-" + name)
数据驱动
需要多次执行一个案例,比如baidu搜索,分别输入中文、英文、数字等进行搜索,这时候需要编写3个案例吗?有没有办法一次运行?
python 的unittest 没有自带数据驱动功能。所以如果使用unittest,同时又想使用数据驱动,那么就可以使用DDT来完成
安装ddt
pip install ddt
ddt使用
class前面@ddt引入ddt
data(x,x ,x )单参
装饰测试方法。参数是一系列的值。
import os
import time
import unittest
from ddt import ddt,data
from selenium import webdriver
#引入ddt
@ddt
class testCase1(unittest.TestCase):
#初始化环境
def setUp(self):
self.driver=webdriver.Chrome()
self.driver.get("https://ww.baidu.com/")
self.driver.maximize_window()
time.sleep(3)
#测试用例test开头会默认自动执行
#data()方法传一系列参数,一个参数不使用unpack
@data("english","毛不易")
def test_baidu4(self,value):
driver = self.driver
driver.find_element_by_id("kw").send_keys(value)
driver.find_element_by_id("su").click()
time.sleep(3)
#清除环境
def tearDown(self):
time.sleep(3)
self.driver.quit()
#主函数
if __name__=="__main__":
unittest.main(verbosity=2)
data([x,x],[x,x])多参
@data(["周迅","周迅_百度搜索"],["张国荣","张国荣_百度搜索"],["张一山","张一山_百度搜索"]) #data多参要使用unpack @unpack def test_baidu4(self,value,expected_value): driver = self.driver driver.find_element_by_id("kw").send_keys(value) driver.find_element_by_id("su").click() time.sleep(3) self.assertEqual(driver.title,expected_value, msg="和预期搜索结果不一致!") time.sleep(3)
file_data(‘xx.json’)传json文件
data.json
[ "english", "毛不易" ]
test001
import os import time import unittest from ddt import ddt, data, unpack, file_data from selenium import webdriver from selenium.common.exceptions import NoAlertPresentException @ddt class testCase1(unittest.TestCase): #初始化环境 def setUp(self): self.driver=webdriver.Chrome() self.driver.get("https://ww.baidu.com/") self.driver.maximize_window() time.sleep(3) #测试用例test开头会默认自动执行 @file_data('data.json') def test_baidu4(self,value): driver = self.driver driver.find_element_by_id("kw").send_keys(value) driver.find_element_by_id("su").click() time.sleep(3) #self.assertEqual(driver.title,expected_value, msg="和预期搜索结果不一致!") time.sleep(3) #普通方法 def is_alert_exists(self): try: self.driver.switch_to.alert except NoAlertPresentException as e: return False return True #清除环境 def tearDown(self): time.sleep(3) self.driver.quit() #主函数 if __name__=="__main__": unittest.main(verbosity=2)
data(“xx(xx.txt)”)传txt文件
data/test_data.txt
data 周迅,周迅_百度搜索 张国荣,张国荣_百度搜索 张一山,张一山_百度搜索import csv import os import sys import time import unittest from ddt import ddt, data, unpack, file_data from selenium import webdriver from selenium.common.exceptions import NoAlertPresentException def getCsv(file_name): rows=[] path=sys.path[0] #txt文档是gbk编码,转变为utf-8 with open(path+'/data/'+file_name, 'rt',encoding='utf-8') as f: readers = csv.reader(f, delimiter=',', quotechar='|') next(readers, None) for row in readers: # [周迅,周迅_百度搜索] temprows=[] for i in row: temprows.append(i) rows.append(temprows) # ([周迅,周迅_百度搜索],[张国荣,张国荣_百度搜索],[张一山,张一山_百度搜索]) return rows @ddt class testCase1(unittest.TestCase): #初始化环境 def setUp(self): self.driver=webdriver.Chrome() self.driver.get("https://ww.baidu.com/") self.driver.maximize_window() time.sleep(3) #测试用例test开头会默认自动执行 @data(*getCsv('test_data.txt')) # ([周迅,周迅_百度搜索],[张国荣,张国荣_百度搜索],[张一山,张一山_百度搜索]) @unpack def test_baidu4(self,value,expected_value): driver = self.driver driver.find_element_by_id("kw").send_keys(value) driver.find_element_by_id("su").click() time.sleep(3) self.assertEqual(driver.title,expected_value, msg="和预期搜索结果不一致!") time.sleep(3) #普通方法 def is_alert_exists(self): try: self.driver.switch_to.alert except NoAlertPresentException as e: return False return True #清除环境 def tearDown(self): time.sleep(3) self.driver.quit() #主函数 if __name__=="__main__": unittest.main(verbosity=2)