1.自动化测试概念
能够代替手工测试的方法和工具都可以称为自动化测试
例如
针对不同的测试对象
web自动化 app自动化 接口自动化
针对不同的测试类型
功能测试自动化 链接测试自动化 性能测试自动化
安全测试自动化
实施自动化测试的目的在于
1.人无法去完成对应的工作或者付出成本非常高
2.可以快速的进行回归测试
2.自动化测试的分类
1.基于代码的自动化 -- 单元测试自动化
2.基于协议的自动化 -- 集成或者接口自动化
3.基于UI的自动化 -- 系统测试的自动化
注:一般情况下
从代码难度讲 自动化代码难度会越来越低
从实施难度讲 实施的难度越来越高,同时变更的影响也会越来也大
3.自动化测试用例的过程
1.制定自动化测试计划
自动化测试的目的
全面自动化
针对部分业务流程和功能的部分自动化
注:全面自动化实施难度会更高
2.整理需要实施自动化的需求
3.将对应的手工测试用例转换为自动化测试用例
4.搭建自动化测试环境
自动化测试环境一般会和手工测试环境分开
注:手工测试环境往往更新会比较快
环境当中也会存在很多脏数据(异常数据)
可能会影响自动化测试脚本执行
5.编写自动化测试脚本
将自动化测试用例通过代码设计自动化测试脚本
自动化测试脚本编写完成会经过调试和运行
将其放入自动化测试用例库
注:一般来说,自动化测试脚本要多次运行
保证能够稳定之后才能放入用例库
注:自动化测试脚本转换为自动化测试用例的比例
需要经过长时间的调试运行(多个迭代版本的调试运行)
相应的转换率才会越来越高
6.执行自动化测试脚本
在执行结束之后,需要对脚本的执行结果进行检查
1.脚本执行错误,分析脚本错误原因
是否是环境或者数据引起,更新到正确的环境下重新运行
如果不是这些情况,从用例库中移除,继续进行调试
2.脚本执行后检查到错误
确认是否是发现bug,一般由测试人员手工执行检查是否是有bug
再次执行自动化脚本进行确认,确认后可以提交bug
如果手工执行没有发现到错误,检查是脚本问题还是环境问题
如果是环境问题,那么更新到正确环境下,重新运行
如果不是,从用例库中移除,继续调试
如果自动化测试脚本的执行通过率不高
那么自动化测试脚本的维护成本就非常高
一般来说,多次执行之后,自动化测试脚本的执行通过率会越来越高
理论上 执行率可以达到100%
通过率会根据软件系统bug情况来确定
注:当然发现越多,说明测试脚本的有效性会越来越好
注:自动化脚本的编写和执行是一个迭代过程
用例的转化率会越来越高,执行率也会越来越高
7.如果发现了bug,进行记录
一般情况下会使用手工和自动化两种方式对缺陷进行回归验证。
4.实施UI自动化需要掌握的技能
1.掌握一门编程语言
java python
2.掌握前端一些知识
html + css + js
3.熟悉网络知识
http协议
4.熟悉自动化测试框架
web框架 selenium robotframework
app框架 appium
5.selenium的版本
现在是selenium3的版本
使用selenium需要安装对应浏览器的驱动程序
使用selenium通过浏览器的驱动,对页面的元素进行操作
通过驱动可以获取服务器返回的响应,通过对响应结果的检查,来确定操作是否正确
注:不同的浏览器的有不同的驱动,同时也要针对浏览器的不同版本
自动化测试脚本selenium
1.selenium自动化框架的版本
2,3的版本主要是通过驱动程序去调用浏览器接口,来实现对页面操作
1的版本通过中间代理的方式,截取浏览器发送的请求,由selenium再组装
请求并接收服务器的响应,这种方式来实现对页面的操作
2,3版本下写的脚本不需要做太多改动,基本上可以运行
但是1版本下的脚本不能在2,3版本下运行
2.页面元素定位的方法
一般通过页面的html代码(浏览器的F12工具)
来查找页面元素对应的代码,获取到对应的定位信息
** 1.通过id来定位**
示例
#test.find_element_by_id('kw').send_keys("软件测试")
test.find_element("id","kw").send_keys("软件测试")
** 2.通过name来定位**
#test.find_element_by_name("wd").send_keys("软件测试")
test.find_element("name","wd").send_keys("软件测试")
** 3.通过class来定位**
#test.find_element_by_class_name("s_ipt").send_keys("软件测试")
test.find_element("class name","s_ipt").send_keys("软件测试")
注:class name之间是空格,不要使用下划线
** 4.通过tag来定位**
这种方式一般不用来去定位页面标签,但是如果页面的元素非常少
可以唯一定位到一个标签的时候,也可以使用
test.find_element_by_tag_name("a").click()
** 5.使用链接的文本来定位 a 标签**
#test.find_element_by_link_text("hao123").click()
test.find_element("link text","hao123").click()
** 6.使用链接的部分文本来定位a标签**
#test.find_element_by_partial_link_text("hao").click()
test.find_element("partial link text","hao").click()
3.使用xpath来定位
** 1.使用绝对路径来定位**
绝对路径是从页面的第一个标签 /html开始
如果在某个路径上,同级出现多个相同的标签,需要写上标签的索引值,索引值从1开始
例如 div[1] div[2]
test.find_element_by_xpath("/html/body/div/form/div[2]/div/input").
send_keys("test")
注:这种方法不推荐使用,除非页面非常简单,页面代码不会经常改动
** 2.使用标签名+属性名的方式**
示例
test.find_element_by_xpath("//input[@name='account']").send_keys("mayi")
标签名由于是绝对路径 标签名前面使用 // 例如这里是 //input
属性名需要加上 @ 例如这里是@name
然后属性的值,因为在一个字符串中,要注意字符串里面的引号使用 例如外面是双引号,里面使用单引号
** 3.先定位到上级标签,再使用上下层级的关系,通过路径来定位**
示例
test.find_element_by_xpath("//form[@method='get']/div[2]/div/input").send_keys("mayi")
这里input标签如果没有属性,那么可以找到能够使用属性定位的上级标签
** 4.如果标签有多个属性,可以通过and运算符将多个属性拼接起来进行定位**
示例
test.find_element_by_xpath("//input[@name='account' and @class='layui-input']").send_keys("mayi")
注:属性一样要加@符号
同样该方法也适用于先定位上级标签再去通过路径去定位
** 5.使用工具获取xpath地址来进行定位**
这里是使用F12工具提供xpath路径地址
//[@id="kw"] 注:这里的 是代表任意标签,使用里面的属性进行定位
xpath同样可以使用定位方式+定位信息这种的方法来定位
示例
test.find_element('xpath','/html/body/div/form/div[2]/div/input').send_keys("test")
元素定位
1.对浏览器的控制
get -- 相对输入url地址,跳转到对应的页面
如果一次性执行多个get方法,会在同一个页面进行多次的跳转
set_window_size(宽,高) -- 会按照设置的宽和高来显示页面的内容窗口
maximize_window() -- 将页面窗口直接最大化的展示
minimize_window() -- 将页面窗口最小化到任务栏
back -- 后退到前一个页面 但是要注意能不能后退
forward -- 前进到下一个页面 但是要注意有能够前进的访问历史
refresh -- 刷新当前页面
示例
test.get("http://192.168.150.131/zentao/user-login.html")
test.get("https://www.baidu.com/")
test.back()
test.forward()
test.find_element('id','kw').send_keys("软件测试")
test.refresh()
2.对页面元素标签的操作
clear -- 清除输入框已经输入的内容
send_keys -- 向输入框输入内容
click -- 模拟用户在页面点击的操作
submit -- 模拟表单的提交,不用于需要触发js脚本的情况
注:如果输入框有内容,直接使用send_keys 会在内容末尾追加一部分
可以先使用clear将内容进行清除,再输入内容
3.获取页面元素的内容和属性
text -- 读取页面元素显示在页面的内容
示例
print(test.find_element('xpath',"//*[@id='login-panel']/div[1]/h4").text)
注:也可以把内容赋值一个变量,然后去检查是否正确
size -- 读取出页面元素的样式大小
示例
print(test.find_element('xpath',"//*[@id='login-panel']/div[1]/h4").size)
get_attribute -- 获取到页面元素某一个属性的值
示例
print(test.find_element('id',"account").get_attribute('class'))
注: text和size在使用时没有括号
4.鼠标的操作模拟
需要先导入对应鼠标操作模块
from selenium.webdriver import ActionChains
记录需要操作的元素的地址信息
adress=test.find_element('xpath','//*[@id="login-form"]/form/table/tbody/tr[4]/td/a')
通过鼠标操作的模块 实现鼠标的模拟
ActionChains(test).click_and_hold(forget_pw).perform()
ActionChains -- 对哪里浏览器实例进行操作
click_and_hold -- 对某个页面元素进行操作
perform -- 释放对应的操作
鼠标的模拟常见有以下几种情况
click_and_hold 左键点击并按住
Release 释放左键
--这两个操作往往是一起实施的
Context_click 右键
Double_click 双击
5.键盘输入的模拟
需要导入键盘模拟的模块
from selenium.webdriver.common.keys import Keys
定位需要模拟键盘操作的页面元素
addr=test.find_element('link text','关于百度')
在页面元素上使用send_keys方法来发起键盘操作
使用 Keys 方法,发送键盘的内容
Keys可以发送键盘上的功能按键
例如 Keys.DOWN 模拟键盘上的方向键
Keys.F1 模拟键盘上的F1
也可以模拟一些组合按键
例如 addr.send_keys(Keys.CONTROL,'a') 模拟键盘上实施 ctrl + a 的全选操作
6.使用模块对下拉框进行操作
导入对应的模块
from selenium.webdriver.support.select import Select
注:也可以使用键盘的模拟来实现对下拉框的操作
如果下拉框的数据每次是动态生成的,那么如果用键盘来模拟
可能选择的数据就不准确
下拉框的选择一共3个方法
1 select_by_index # 通过索引定位 通过下拉框中值的索引 从0开始
2 select_by_value # 通过value值定位 通过下拉框的标签 value属性值来定位
示例
<option value="normal" selected="selected" data-keys="zhengchang zc">正常</option>3 select_by_visible_text # 通过文本值定位,通过标签的文本值来定位
示例
上个示例标签的 正常 就是标签的文本
7.获取title和url
title和url对一个页面而言,不需要针对页面的标签进行操作
可以直接用浏览器对象来操作
示例
print(test.title) -- 获取title
print(test.current_url) --获取url
注:可以通过title或url来验证是否跳转到正确页面
8.implicitly_wait方法
这个方法会设置超时的时间值,在这个时间值内,如果服务器返回了响应
代码会立刻开始执行
如果服务器返回的时间超过设置的值,程序会报错
这个方法和sleep不同在于,sleep是将代码停止执行
等待时间结束后, 继续执行剩下的代码
注:一般情况下使用implicitly_wait方法
但是在有些情况可能会需要使用sleep来等待页面数据处理和返回
在编写脚本的过程中,可以灵活的使用这两个方法
9.切换到内嵌的子页面
使用switch_to.frame(定位信息)
去切换到内嵌的页面去
示例
frame_addr=test.find_element('id','loginIframe')
test.switch_to.frame(frame_addr)
切换回外层的页面
切换到上一级页面
switch_to.parent_frame()
切换到最外层的页面
switch_to.default_content()
10.网页的切换
chrome每打开一个网页,会开启对应新的进程
要切换网页,需要先获取当前打开的网页 current_window_handle 标识
然后开打新的页面之后,获取所有的页面标识 window_handles
注:所有的页面标识实际上一个列表
可以通过列表的读取,排除当前的页面的标识
使用 switch_to.window(标识) 切换到新打开页面的标识
注:网页的打开和关闭是由操作系统来管理的
每次打开网页都需要记录对应的值,因为每次打开网页都会有一个不同的标识
如果需要打开多个网页,可以每一个单独的去记录
11.页面的关闭和浏览器的关闭
close -- 关闭当前打开的页面
quit -- 退出浏览器
12.警告框的处理
在网页中,做出一些操作之后,页面会弹出一个警告框
这种警告框一般情况下,弹出后不能继续操作页面
需要使用 switch_to_alert() 跳转到警告框
警告框一共分三种,都是用这个方法来跳转
alert : 只有一个确定按钮
confirm: 由确定和取消按钮
prompt: 有文字输入框
可以通过跳转的方法,使用
test.switch_to_alert().text text 打印出警告框中的文本
test.switch_to_alert().accept() 相当于点击这个确定按钮
test.switch_to_alert().dismiss() 相当于关闭或者取消
test.switch_to_alert().send_keys("软件测试") 在警告框中的输入框中输入内容
注:该方法只对prompt这种类型警告框有效
警告框是没有定位消息,使用 switch_to_alert()直接进行处理
12.操作浏览器的cookie信息
注意cookie信息的格式
在请求中可能存在多个cookie
可以使用
test.get_cookies() 获取当前页面的所有cookie信息
格式是以 列表和字典,cookie以列表中元素的形式存在
每个cookie有多个键值对,以字典的形式存在
示例
[
{'domain': '.baidu.com', 'expiry': 1637739952, 'httpOnly': False, 'name': 'BA_HECTOR', 'path': '/', 'secure': False, 'value': 'at258h848h808heka31gprnt10r'},
{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '35105_31254_35049_34584_34517_35233_34578_35167_34814_26350_35145'},
{'domain': '.baidu.com', 'expiry': 1669272352, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': '0656D86D246547F6839C047B2C580A05:FG=1'},
{'domain': '.baidu.com', 'expiry': 3785219999, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': '0656D86D246547F6786AF018BBEFDE6C'},
{'domain': '.baidu.com', 'expiry': 3785219999, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1637736351'},
{'domain': 'www.baidu.com', 'expiry': 1638600352, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '12314753'},
{'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '1'}
]
可以用 add_cookie(cookie_dict) 方法来添加cookie信息
注意的是,cookie信息应该以字典形式存在,要保证键值对的完整
示例
test.add_cookie({'name':'BDUSS','value':'TE1bmFOOG1QcHhydzRvQ3BjbFh2REZkY2NIeEtDQXlsejdEUC1kdUNmb3hhOFZoSVFBQUFBJCQAAAAAAAAAAAEAAAAX0fMea28xOTgwMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADHenWEx3p1hR'})
添加cookie之后,可以使用refresh这个方法,刷新页面让cookie生效
实际上向服务器发送请求,在请求中携带添加的cookie信息
可以使用 get_cookie(name): 读取某个cookie信息
需要写入cookie中name的值
示例
print(test.get_cookie('PSTM'))
delete_cookie(name,optionsString)
删除cookie信息,name是key的名称,optionsString是该cookie的选项,包括路径,域等信息
delete_all_cookies() 直接删除所有的cookie信息
unittest框架
1.构建自动化脚本测试工程
1.需要使用方法去验证脚本执行成功
2.同时记录脚本执行失败的情况
3.实现对测试脚本的管理
4.实现对测试数据的管理
2.使用unittest框架来记录脚本的执行情况
构建测试的类,继承于unittest中的TestCase这个类
class t_calc(unittest.TestCase):
def setUp(self) -> None:
print("用例执行开始")
设计一个方法,对某个类的对象中一个方法结果进行验证
可以理解为这是一个测试用例
def test_add(self):
生成这个要测试的实例
t1=calc(10,20)
使用unittest当中的断言,对实例的方法返回的结果进行验证
如果验证通过,这个用例会在结果当中记录为通过
如果验证不通过或者执行过程的当中发生错误,会记录不通过或者记录发生的错误情况
self.assertEqual(t1.add(),30)
def test_sub(self):
t2=calc('a',20)
self.assertEqual(t2.add(),200)
def tearDown(self) -> None:
print("用例执行结束")
执行结果有
. -- 代表这个用例执行通过
F -- 代表这个用例执行不通过,和预期结果不相同
E -- 代表这个用例执行中发生了错误
self.assertEqual 这个方法称为断言,继承于unittest中的TestCase这个类
断言方法有很多种,根据实际情况来使用
assertEqual 两个值是否相等,如果相等,则通过,不相等记为不通过
setUp -- 称为初始化方法,可以理解为每个用例执行之前需要执行一次
tearDown --做一些清理和结束的工作,每个用例执行之后需要执行一次
这两个方法的名字是固定的,不能修改
def test_add(self): 每个用例的方法名前面需要使用 test 做为开头
2.测试方法的执行
在代码的末尾使用
if name=="main":
unittest.main()
会将所有的被测试的脚本全部执行
通过构建测试套件,选择对应的用例来执行
if name=="main":
#生成一个测试套件
suit=unittest.TestSuite()
#在测试套件中添加需要执行的用例
#使用addTest方法
#用例格式使用的是 类名('方法名') 方法名要做为字符串来导入
suit.addTest(t_calc("test_add"))
#生成一个用例的执行实例
runner=unittest.TextTestRunner()
#使用执行的实例去执行测试套件中的用例
runner.run(suit)
3.使用HTMLTestRunner生成测试报告
实际上HTMLTestRunner是继承于unittest的,同样需要生成测试套件
并使用TextTestRunner去执行套件中的用例,但是它会将执行的结果收集起来
使用html模板来生成网页的报告
导入对应的模块
from HTMLTestRunner import HTMLTestRunner
在生成模块的实例
#htmltestrunner会写入一个文件,需要指定文件写入路径 文件名 和内容写入方式
fr = open('C:/Users/mycom/Desktop/testcalc01.html', 'wb')
#使用htmltestrunner生成用例的执行器,htmltestrunner会自动捕获用例的执行情况并进行一个记录
#stream -- 指定需要写入的文件对象
#title -- html页面title
#description -- 网页body中的提示信息
runner = HTMLTestRunner(stream=fr, title='报告', description='用例执行情况')
#使用runner去执行测试套件
runner.run(suit)
#文件写完了,关闭文件读写通道
fr.close()
版权归原作者 小双221 所有, 如有侵权,请联系我们删除。