一、web 自动化
1.自动化
1.1概念
由机器设备代替人工,自动完成制定目标的过程
1.2优点
- 减少人工劳动力
- 提高工作效率
- 产品规格统一标准
- 规模化(批量生产)
- 安全
2.自动化测试
2.1 概念
用程序代替人工去执行测试的过程
软件测试:对程序进行操作,以发现程序错误,并验证其是否满足需求过程
2.2 应用场景
- 解决:冒烟测试
- 冒烟测试:每个版本提测时,验证其是否达到可测标准
- 解决:回顾测试
- 回归测试:可以理解多用户同时去操作软件,统计软件服务进行验证
- 解决:压力测试
- 压力测试:可以理解多用户同时去操作软件,统计软件服务器处理多用户请求的能力
- 解决:兼容性测试
- 兼容性测试:不同浏览器
- 提高测试效率吗,保证产品质量
2.3正确认识
优点
- 较少的时间内运行更多的测试用例
- 自动化脚本可重复运行
- 较少人为的错误
- 克服手工测试的局限性
误区 - 自动化测试可以完全替代手工测试
- 自动化测试一点比手工测试厉害
- 自动化测试可以挖掘更多的bug
- 自动化测试适用于所有功能
2.4 分类
- web-自动化测试
- 移动-自动化测试
- 接口-自动化测试
- 单元测试-自动化测试
3.web自动化测试
概念
用程序代替人工去执行web测试的过程
什么样的项目适合去做web自动化测试?
- 需求变动不频繁
- 项目周期长
- 项目需要回归测试/冒烟测试/兼容性测试/压力测试
web自动化测试什么时候开始
- 对于自动化冒烟测试,一般是手工测试开始前
- 对与其他目的的自动化测试,一般是手工测试结束后
web自动化测试所属分类
属于功能测试(黑盒)
4.Selenium
4.1 web自动化工具简介
主流工具
- QTP:一个商业化的功能测试工具,收费,支持web、桌面自动化测试
- Selenium:开源的web自动化测试工具,主要做功能测试
#- robot framework:一个基于python可扩展的关键字驱动的自动化测试框架
Selenium特点
- 开源软件:源代码开放可以根据需要来增加工具的某些功能
- 跨平台:Linux、Windows、Mac
- 支持多种浏览器
- 支持多种语言
- 成熟稳定
- 功能强大
4.2环境搭建
1. 搭建步骤
前提:基于python搭建
- python开发环境
- 安装 Selenium包
- 安装浏览器
- 安装浏览器驱动
2.安装Selenium包
pip install selenium
3.安装浏览器驱动
4.3 入门示例
1.需求
2.实现步骤
3.示例代码
# 1.导包
import time
from selenium import webdriver
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.打开百度首页
driver.get("http://www.baidu.com")
# 4.暂停3s
time.sleep(3)
# 5.关闭浏览器
driver.quit()
二、selenium-API
1.元素定位基础
1.1 为什么进行元素定位
让程序操作指定元素,就必须先找到此元素
1.2 如何进行元素定位
通过元素的信息或元素层级结构来定位元素
1.3 浏览器开发工具
概念
浏览器开发者工具就是给专业的web应用和网站开发的人员使用的工具,包含了对html查看和编辑,Javascript控制台,网络状况监视等功能,是开发Javascript、css、html和Ajax的得力助手
作用
定位元素,查看元素信息
1.4 元素定位方式
1.4.1总体介绍
Selenium提供了八种定位元素方式
- id
- name
- class_name
- tag_name
- link_text
- partial_link_text
- XPath
- css
1.4.2 id定位
说明:id定位就是通过元素的id属性来定位元素,html规定id属性在整个html文档中必须是唯一的;
前提:元素有id属性
方法
driver.find_element(by=By.ID, value="id")
案例
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element(by=By.ID, value="userA").send_keys("admin")
driver.find_element(by=By.ID, value="passwordA").send_keys("123456")
# 4.暂停5s
time.sleep(3)
# 5.关闭浏览器
driver.quit()
1.4.3 name定位
说明:name定位就是根据元素name属性来定位,html文档中name的属性值可以重复
前提:元素有name属性
方法
driver.find_element(by=By.NAME, value="id")
代码
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element(by=By.NAME, value="userA").send_keys("admin")
driver.find_element(by=By.NAME, value="passwordA").send_keys("123456")
# 4.暂停5s
time.sleep(5)
# 5.关闭浏览器
driver.quit()
1.4.4 class_name定位
说明:class_name定位就是根据元素class属性值来定位元素,html通过class来定义元素的样式
前提:元素有class属性
注意:如果class有多个属性值,只能使用其中一个
方法
driver.find_element(by=By.CLASS_NAME, value="class_name")
代码
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element(by=By.CLASS_NAME, value="telA").send_keys("13111111111")
driver.find_element(by=By.CLASS_NAME, value="emailA ").send_keys("123456@qq.com")
# 4.暂停5s
time.sleep(5)
# 5.关闭浏览器
driver.quit()
1.4.5 tag_name定位
说明:tag_name定位就是通过标签名来定位;
html本质就是由不同的tag组成,每一种标签一般在页面中会存在多个所以不方便进步精确定位,一般很少使用
如何获取第二个标签
方法
driver.find_element(by=By.TAG_NAME, value="telA")
实现
1.4.6 link_text定位
说明:是专门用来定位超链接元素的<a>连接</a>并且是通过超链接的文本来定位元素
方法
driver.find_element(by=By.LINK_TEXT, value="LINK_TEXT")
# LINK_TEXT:为超链接全部文本内容
实现
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element(by=By.LINK_TEXT, value="访问 新浪 网站").click()
# 4.暂停5s
time.sleep(5)
# 5.关闭浏览器
driver.quit()
partial_link_test 定位
说明:partial_link_test 定位是对link_test定位的补充,
link_text使用全部文本内容匹配元素,
而partial_link_test 可以使用局部来匹配元素,也可以全部文本内匹配元素
方法
driver.find_element(by=By.PARTIAL_LINK_TEXT, value="PARTIAL_LINK_TEXT")
# PARTIAL_LINK_TEXT 可以传入a标签局部文本-能表达唯一性
实现
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element(by=By.PARTIAL_LINK_TEXT, value="访问 新浪 网站").click() # 全部文本点击
driver.find_element(by=By.PARTIAL_LINK_TEXT, value="访问").click() # 局部文本点击
# 4.暂停5s
time.sleep(5)
# 5.关闭浏览器
driver.quit()
1.5 定位一组元素
说明:
方法
driver.find_elements(by=By.TAG_NAME, value="input")
#作用:
# 1.查找定位所有符合条件的元素
# 2.返回值是一个列表
#说明:列表数据格式的读取需要指定下标(下标从0开始)
实现
# 1.导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
element = driver.find_elements(by=By.TAG_NAME, value="input")
element[2].send_keys("123456")
# 4.暂停5s
time.sleep(5)
# 5.关闭浏览器
driver.quit()
2.元素定位-XPath/CSS
2.1 总体介绍
1. 如果id,name,class属性,该如何定位?
2. 如果通过name,class等无法定位到唯一元素,该如何进行定位?
2.2 XPath定位方式
2.2.1 什么是XPath
1. XPath即为xml,path的简称,他是一门在xml文档中查找元素信息的语言
2. html可以看做是xml的一种实现,所以Selenium用户可以使用这种强大的语言在web应用中定位元素
2.2.2 xpath定位方式
2.2.2.1总体介绍
四种定位方式
1. 路径-定位
2. 利用元素属性-定位
3. 属性与逻辑结合-定位
4. 层级与属性结合-定位
方法
driver.find_element(by=By.XPATH, value="XPATH")
2.2.2.2 路径
绝对路径: 从最外层元素指定元素之间所有经过元素层级的路径
1.绝对路径以/html根节点开始,使用/来分割元素层级
如:/html/boby/div/fields/p[1]/input
2.绝对路径对页面结构要求比较严格,不建议使用
相对路径: 匹配任意层级的元素,不限制元素的位置
1.相对路径以//开始
2.格式://input或者//*
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
driver.find_element(by=By.XPATH, value="/html/body/div/fieldset/form/p[1]/input").send_keys("admin")
# 暂停三秒
time.sleep(3)
driver.find_element(by=By.XPATH, value="//*[@id='passwordA']").send_keys("123")
# 暂停5秒
time.sleep(5)
driver.close()
使用谷歌浏览器获取xpath表达式的过程:
1.元素上邮件–>检查
2.在文档F12对应的元素上右键–>copy–>copy xpath或者copy full xpath
2.2.2.3 利用元素属性
说明:通过使用元素的属性信息来定位元素
格式://input[@id='userA'] 或者 //*[@id=''userA]
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.XPATH, value="//*[@id='userA']").send_keys("admin")
driver.find_element(by=By.XPATH, value="//*[@name='userA']").send_keys("admin")
driver.find_element(by=By.XPATH, value="//*[@placeholder='请输入用户']").send_keys("admin")
driver.find_element(by=By.XPATH, value="//*[@type='text']").send_keys("admin")
# 暂停三秒
time.sleep(3)
driver.find_element(by=By.XPATH, value="//*[@id='passwordA']").send_keys("123")
# 暂停5秒
time.sleep(5)
driver.close()
2.2.2.4 属性与逻辑结合
说明:解决元素之间存在相同属性值的问题
格式://*[@name="xxx" and @class='yyy']
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.XPATH, value="//*[@type='text' and @name='userA']").send_keys("admin")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.2.2.5 层级与属性结合
说明:如果通过元素自身的属性不方便直接定位该元素,则可以先定位到其父元素,然后再找到该元素的
格式://*[@id='p1']/input
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.XPATH, value="//*[@id='p1']/input").send_keys("admin")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.2.2.6 XPath 扩展
说明:不适用函数时://*[@id='xxx']
使用函数后:
//*[text()='xxx']文本内容是xxx的元素
//*[contains[@(attribute,'xxx')]属性中含有xxx值的元素
//*[start-with(@attribute,'xxx')]属性以xxx开头的元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.XPATH, value="//*[text()='访问 新浪 网站']").click()
driver.find_element(by=By.XPATH, value="//*[contains(@placeholder,'用户名')]").send_keys("admin")
driver.find_element(by=By.XPATH, value="//*[starts-with(@placeholder,'请输入密')]").send_keys("1234")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3 css定位
2.3.1 什么是css
- css是一种语言,它用来描述HTML元素的显示样式
- 在css中,选择器是一种模式,用于选择添加样式的元素
- Selenium中可以使用这种选择器来定位元素
提示
1.在Selenium中推荐使用css定位,因此他比XPath定位速度要快
2.css选择器语法非常强大,在这里我们只学习在测试中常用的几个
2.3.2 css定位方式
2.3.2.1总体介绍
常用定位方法
1. id选择器
2. class选择器
3. 元素选择器
4. 属性选择器
5.层级选择器
方法
driver.find_element(by=By.CSS_SELECTOR, value="CSS_SELECTOR")
2.3.2.2 id选择器
说明:根据元素的id属性来选择
格式:#id(值)
例如:#userA 选择id属性值为userA的元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.CSS_SELECTOR, value="#userA").send_keys("admin")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3.2.3 class选择器
说明:根据元素的class属性来选择
格式:.class(值)
例如:.userA 选择class属性值为userA的元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.CSS_SELECTOR, value=".telA").send_keys("13111111111")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3.2.4 元素选择器
说明:根据元素的标签名来选择
格式:标签名
例如:input 选择input元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.CSS_SELECTOR, value="button").click()
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3.2.5 属性选择器
说明:根据元素的属性名和值来选择
格式:[attribute=value]
例如:[type="password"]选择type属性名对应的属性值为password的元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过XPath
driver.find_element(by=By.CSS_SELECTOR, value="[type='password']").send_keys("123456")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3.2.6 层级选择器
说明:根据元素的父子关系来选择
格式:element1>element2 通过element1来定位element2,并且element2必须为element1的直接子元素
例如1:p[id='p1']>input 定位了指定p元素下的直接子元素
格式2:element1 element2 通过element1来定位element2,并且element2为element1的后代元素
例如2:p[id='p1'] input 定位了指定p元素下的后代元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过css
driver.find_element(by=By.CSS_SELECTOR, value="p[id='pa']>input").send_keys("admin")
driver.find_element(by=By.CSS_SELECTOR, value="div[class='zc'] input").send_keys("admin")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
2.3.2.7 css扩展
格式1: input[type^='p'] type属性以p字母开头的元素
格式2: input[type$='d'] type属性以b字母结束的元素
格式3: input[type*='w'] type属性包含w字母的元素
练习
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 利用元素属性通过css定位扩展的方式
# 开头
driver.find_element(by=By.CSS_SELECTOR, value="input[type^='t']").send_keys("admin")
driver.find_element(by=By.CSS_SELECTOR, value="input[name^='u']").send_keys("admin")
# 结尾
driver.find_element(by=By.CSS_SELECTOR, value="input[type$='t']").send_keys("admin")
# 包含
driver.find_element(by=By.CSS_SELECTOR, value="input[type*='ex']").send_keys("admin")
# 暂停三秒
# 暂停5秒
time.sleep(5)
driver.close()
XPath 和CSS对比
定位方式 | XPath | css |
---|---|---|
元素名 | //input | input |
id | //*[@id=‘userA’] | #userA |
class | //*[@class=‘userA’] | .telA |
属性名 | 1.//input[@id=‘userA’ 2.//[text()=‘x’]] 3.//*[contains(@attribute,''x)] | 格式1: input[type^=‘p’] type属性以p字母开头的元素 格式2: input[type$=‘d’] type属性以b字母结束的元素格式3: input[type*=‘w’] type属性包含w字母的元素 |
3.元素定位总结
3.1.方法介绍
1. id,name,class_name:为元素属性定位
2. tag_name:为元素标签名称
3. link_text、partial_link_text:为超链接定位(a标签)
4. XPath:为元素路径定位
5. css:为css选择器定位
4.元素操作
4.1应用场景
1. 需要让脚本模拟用户给指定元素输入值
2. 需要让脚本模拟人为删除元素的内容
3. 需要让脚本模拟点击操作
4.2方法
1. click() 单击元素
2. send_keys(value)模拟用户输入
3. clear() 清除文本
5.浏览器操作
示例
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框 admin
# 浏览器最大化窗口
driver.maximize_window()
# 设置窗口的大小
driver.set_window_size(300, 300)
# 设置窗口位置
driver.set_window_position(300, 300)
# 前进
driver.back()
# 后退
driver.forward()
# 刷新
driver.refresh()
#
print("title为:", driver.title)
print("当前页面地址", driver.current_url)
driver.find_element(by=By.CSS_SELECTOR, value="input[type^='t']").send_keys("admin")
# 暂停5秒
time.sleep(5)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
6.获取元素信息
6.1应用场景
1. 获取元素的文本
2. 获取元素属性值
3. 让程序判断元素是否可见状态
6.2 常用方法
1. size 返回元素大小
2. text 获取元素的文本
3. get_attribute("XXX") 获取属性值,传递参数元素的属性名
4. is_displayed() 判断元素是否可见
5. is_enabled() 判断元素是否可用
6. is_selected() 判断元素是否选中,用来检查复选框或单选按钮是否被选中
提示:size、text:为属性,调用时无括号;如:xxx.size
6.3案例
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 获取用户名输入框的大小
print(driver.find_element(by=By.ID, value="userA").size)
# 获取页面上第一个超链接的文本内容
print(driver.find_element(by=By.TAG_NAME, value="a").text)
# 获取页面上第一个超链接的地址
print(driver.find_element(by=By.TAG_NAME, value="a").get_attribute("href"))
# 判断页面中的span的标签是否可见
print(driver.find_element(by=By.TAG_NAME, value="span").is_displayed())
# 判断页面中的按钮是否可用
print(driver.find_element(by=By.ID, value="cancelA").is_enabled())
# 判断页面中的旅游对应的复选框是否为选中状态
print(driver.find_element(by=By.ID, value="lyA").is_selected())
time.sleep(5)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
7.鼠标操作
7.1 什么是鼠标操作
点击、右击、双击、悬停、拖拽
7.2 应用场景
现在web产品中存在丰富的鼠标交互方式,作为一个web自动化测试框架,需要对这些鼠标操作的应用场景
7.3 常用方法
#说明:在Selenium中将操作鼠标的方法封装在ActionChains类中
# 实例化对象:
from selenium.webdriver import ActionChains
action = ActionChains(driver)
#方法:
action.context_click(element) # 右击
action.click(element) # 双击
action.move_to_element(element) # 悬停
action.drag_and_drop(element) # 拖拽
action.perform(source,target) # 执行
# 为了更好的学习其他方法,我们先学习perfrom()执行方法,因为ActionChains所有方法需要执行才能生效
7.4 鼠标执行-perform()
说明:在ActionChains类中所有提供的鼠标事件方法,在调用的时候,所有行为都存储在Actionchains对象中,而perform()方法就去执行所有鼠标事件
强调:必须调用perform()方法才能执行鼠标事件
7.5 鼠标右键-context_click()
说明:对与鼠标右键,如果弹出的是浏览器默认的菜单,Selenium没有提供菜单选项的方法;
如果是自定义的右键菜单,则可以通过元素定位开操作菜单中的选项
练习
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框
element = driver.find_element(by=By.ID, value="userA")
# 执行右键点击操作
action = ActionChains(driver)
action.context_click(element).perform()
time.sleep(5)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
7.6 鼠标双击-double_click()
说明:模拟鼠标双击左键的操作
练习
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 需求:打开A界面,在用户名文本框上点击鼠标右键
element = driver.find_element(by=By.ID, value="userA")
element.send_keys("admin")
time.sleep(5)
action = ActionChains(driver)
action.double_click(element).perform()
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
7.7 鼠标拖动-drag_and_drop()
说明:
练习
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/drag.html")
# 需求:打开A界面,在用户名文本框上点击鼠标右键
red = driver.find_element(by=By.ID, value="div1")
blue = driver.find_element(by=By.ID, value="div2")
ActionChains(driver).drag_and_drop(red, blue).perform()
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
7.8 鼠标悬停-move_to_elemet()
说明:模拟鼠标拖动动作,选定拖动源元素释放到目标元素
关键点分析:
1. 源元素
2. 目标元素
3. 调用方法
练习
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 需求:打开A界面,在用户名文本框上点击鼠标右键
element = driver.find_element(by=By.TAG_NAME, value="button")
action = ActionChains(driver)
action.move_to_element(element).perform()
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
8.键盘操作
8.1应用场景
说明:
1. 模拟键盘上的按键或者组合键的输入,如复制粘贴
2. Selenium把键盘的按键都封装在key类中
8.2 常用操作
# 导包
from selenium.webdriver.common.keys import Keys
# 1.删除键(backspace)
send_keys(Keys.BACK_SPACE)
# 2.空格键 (space)
send_keys(Keys.SPACE)
# 3.制表键(tab)
send_keys(Keys.TAB)
# 4.回退键(ESCAPE)
send_keys(Keys.ESCAPE)
# 5.回车键(Enter)
send_keys(Keys.ENTER)
# 6.全选(Ctrl+A)
send_keys(Keys.CONTROL, 'a')
# 3.制表键
send_keys(Keys.CONTROL, 'c')
8.3 案例代码
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 需求:打开A界面,在用户名文本框上点击鼠标右键
# 1.删除键(backspace)
element = driver.find_element(by=By.ID, value="userA")
element.send_keys("admin1")
time.sleep(3)
# 删除1
element.send_keys(Keys.BACK_SPACE)
time.sleep(3)
# 全选用户名
element.send_keys(Keys.CONTROL, 'a')
time.sleep(3)
# 复制用户名
element.send_keys(Keys.CONTROL, 'c')
time.sleep(3)
# 粘贴到电话栏
driver.find_element(by=By.ID, value="telA").send_keys(Keys.CONTROL, 'v')
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
9.元素等待
9.1 概念
概念
定位元素,如果未找到,在指定时间内一直等待的过程
分类:
隐式等待
显示等待
9.2应用场景
由于一些原因,我们想找元素没有立刻出来,此时如果直接定位会报错
- 网络速度慢
- 服务器处理请求速度慢
- 硬件配置差
思考:是否定位每个元素时,都需要元素等待
9.3隐式等待
9.3.1方法
driver.implicitly_wait(10)
# 隐式等待为全局设置(只需要设置一次,会作用于所有元素)
# 参数:
# timeout:超时时长,单位:秒
9.3.2案例
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 需求:打开A界面,使用隐式等待定位延时加载的输入框,如果元素存,并输入admin
driver.implicitly_wait(10)
driver.find_element(by=By.CSS_SELECTOR, value="input[placeholder='延时加载的输入框']").send_keys("admin")
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
注意
单个元素定位超时会报错
NoSuchElementExpection
9.4显示等待
9.4.1 方法
说明:在Selenium中把显式等待的想干方法封装在webdriverwait中
# 显示等待,为定位不同的元素超时时间设置不同的值
1.导包
2. webDriverWait(driver,timeout,poll_frequency=0.5)
1.driver:浏览器驱动对象
2.timeout:超时时长,单位,秒
3.poll_frequency: 检测的间隔时间,默认为0.5s
3. 调用until(method)
1. metohd:函数名称,该函数用来实现元素定位
2. 一般使用匿名函数来实现:lambda x: x.find_element(by=By.XXX, value="XXX")
8.4.2 案例
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 需求:打开A界面,使用隐式等待定位延时加载的输入框,如果元素存,并输入admin
wait = WebDriverWait(driver, 10, 1)
element = wait.until(lambda x: x.find_element(by=By.CSS_SELECTOR, value="input[placeholder='延时加载的输入框']"))
element.send_keys("admin")
# driver.find_element(by=By.CSS_SELECTOR, value="input[placeholder='延时加载的输入框']").send_keys("admin")
time.sleep(3)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
9.5隐式和显式区别
- 作用域:隐式等待为全局有效,显式等待为单个元素有效
- 使用方法:隐式等待直接通过趋同对象调用,而显式等待方法封装在WebDriverWait类中
- 达到最大超时时长后抛出异常不同:隐式等待为NoSuchElementException,显式等待为:TimeoutException
10.下拉框/弹出框/滚动条操作
10.1下拉框
10.1.1 方法一
代码
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
# 需求:打开页面,完成操作
# 1.暂停2s,选择广州
# 2.暂停3s,选择上海
# 3.暂停4s,选择北京
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1.
time.sleep(3)
driver.find_element(by=By.XPATH, value="//*[@id='selectA']/option[3]").click()
time.sleep(2)
driver.find_element(by=By.XPATH, value="//*[@id='selectA']/option[2]").click()
time.sleep(2)
driver.find_element(by=By.XPATH, value="//*[@id='selectA']/option[1]").click()
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
10.1.2 方法二
说明:select类时Selenium为操作select标签封装的
实例化对象:
select = select(element)
element:<select>标签对应的元素,通过元素指引
操作方法:
1.select_by_index(index) 根据option索引来定位,从0开始
2.select_by_value(value) 根据option属性value的值来定位
3.select_by_visible_text(text) 根据option中文本内容来定位
实现
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
# 需求:打开页面,完成操作
# 1.暂停2s,选择广州
# 2.暂停3s,选择上海
# 3.暂停4s,选择北京
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
select = Select(driver.find_element(by=By.ID, value="selectA"))
time.sleep(2)
select.select_by_index(2)
time.sleep(2)
select.select_by_value("sh")
time.sleep(2)
select.select_by_visible_text("北京")
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
10.2弹出框
10.2.1 分类
alert:警告框
confirm:确认框
prompt:提示框
10.2.2 案例
10.2.3方法
1.获取弹出框对象
alert = driver.switch_to.alert
2.调用
alert.text #返回alert/confirm/prompt文字信息
alert.accept() # 接受对话框选项(确认)
alert.dismiss() # 取消对话框选项(取消)
10.2.4 代码
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1
driver.find_element(by=By.ID, value="alerta").click()
time.sleep(2)
alert = driver.switch_to.alert
print(alert.text)
time.sleep(2)
alert.accept() # 接受对话框选项(确认)
time.sleep(2)
# alert.dismiss() # 取消对话框选项
driver.find_element(by=By.ID, value="userA").send_keys("admin")
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
10.3 滚动条
10.3.1 应用场景
在HTML中,由于前端技术更新,很多网站内容都是动态加载的,如页面注册同意条款,
10.3.2实现方式
说明:Selenium中没有提供滚动条的操作方法,但是他提供了JS的方法
1.设置JS脚本控制滚动条
JS = “window.scrollTO(0,1000)”
2.Selenium调用执行JS脚本的方法
driver.excuse_script
10.3.3 案例
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8CA.html")
# js 滚动到最底部
js1 = "window.scrollTo(0,10000)"
# js滚动到最顶部
js2 = "window.scrollTo(0,0)"
time.sleep(2)
driver.execute_script(js1)
time.sleep(2)
driver.execute_script(js2)
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
11.frame切换
11.1概念
HTML中的一种框架,主要作用是当前页面中指定区域显示另一页元素:
形式一:[了解]
<frameset>
<frame src="a.html" >
</frameset>
形式二:
<iframe name="a" src="b.html" widh="200" hight="200" ></iframe>
11.2方法
driver.switch_to.frame(name) # 切换到指定frame
# name,可以传 id,name定位的frame元素
driver.switch_to.default_content() # 恢复默认页面
11.3代码
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
driver.find_element(by=By.ID, value="userA").send_keys("admin")
time.sleep(2)
driver.switch_to.frame("idframe1")
driver.find_element(by=By.ID, value="userA").send_keys("adminA")
time.sleep(2)
driver.switch_to.default_content() # 恢复默认页面
time.sleep(2)
driver.switch_to.frame("idframe2")
driver.find_element(by=By.ID, value="userA").send_keys("adminB")
# driver.find_element(by=By.XPATH, value="//*[@id='userA']").send_keys("adminA") # 这一步出错!!!
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
# driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
driver.quit()
12.多窗口切换
12.1 概念
在HTML页面中,当点击超链接或按钮时,有的会在新窗口打开页面
12.2方法
# 说明:在Selenium 中封装了获取当前窗口句柄,获取所有窗口句柄和切换到指定句柄窗口的方法
# 句柄:窗口唯一识别码
# 方法
driver.current_window_handle # 获取当前窗口句柄
driver.window_handles # 获取所有窗口句柄
driver.switch_to.window(handle) # 切换到指定句柄的窗口
12.3代码
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
window = driver.current_window_handle # 获取当前窗口句柄
print(driver.current_window_handle)
driver.find_element(by=By.ID, value="fw").click()
print("所有窗口句柄", driver.window_handles)
handles = driver.window_handles
driver.switch_to.window(handles[1]) # 切换到指定句柄的窗口
driver.find_element(by=By.CLASS_NAME, value="inp-txt").send_keys("新浪搜索")
time.sleep(2)
driver.switch_to.window(handles[0]) # 切换到指定句柄的窗口
driver.find_element(by=By.ID, value="userA").send_keys("admin")
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
# driver.quit()
13.窗口截图
13.1 概念
为什么要窗口截图
有时候打印的错误信息不十分准确,需要窗口截图辅助定位错误
13.2 方法
说明: 在Selenium中提供截图方法,我们只需要调用即可
方法:
driver.get_screenshot_as_file(path) # 图片保存目录
13.3 代码
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("file:///E:/pagetest/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
driver.find_element(by=By.ID, value="userA").send_keys("admin")
time.sleep(1)
driver.get_screenshot_as_file("../png/123.png") # 图片保存目录
time.sleep(2)
# 关闭当前浏览器窗口 ==>执行结果,留下留下新浪网站,关闭注册页
driver.close()
# 关闭浏览器驱动对象 ==>关闭所有窗口
# driver.quit()
14.验证码处理
14.1 概念
一种随机生成的信息,为了防止恶意的请求行为,增加应用的安全性
为什么学习验证码?
大部分系统在用户登录注册的时候要求输入验证码,而我们自动化脚本也会面临这个问题
14.2 常用方法
方法:
1.去掉验证码
2.设置万能验证码
3.验证码识别技术:成功率不高,验证码种类繁多,不太适合
4.记录cookie:比较实用
14.3cookie
14.3.1概念
- 由web服务器生成,并且保存在用户浏览器上的小文本文件,他可以包含用户相关信息
- cookie数据格式是键值对的格式
- 产生:客户端请求服务器,如果该用户需要记录该用户状态,就向客户端浏览器颁发一个cookie数据
- 使用:当浏览器再次请求该网站时,浏览器把请求的数据和cookie数据一同提交给服务器,服务器检查该cookie,以用来辨别用户状态
14.3.2 应用场景
- 实现会话跟踪,记录用户登录状态
- 实现记住密码和自动登录的功能
- 用户未登录的状态下,记录购物车中的商品
14.4 Selenium操作cookie
14.4.1 方法
说明:Selenium中对cookie操作提供相关的方法
方法:
1. get_cookie(name) -->获取指定cookie
2. get_cookies() -->获取本网站所有本地cookies
3. add_cookie(cookie_dict) -->添加cookie
一个字典对象,必选的键包括:“name” and “value”
14.4.2 代码
import time
from selenium import webdriver
# 2.创建驱动对象
driver = webdriver.Chrome()
# 3.业务操作
driver.get("https://www.baidu.com")
# print(driver.get_cookie(name="BDUSS"))
driver.add_cookie({"name": "BDUSS", "value":""})
# driver.get_cookies() #获取所有Cookie
driver.refresh()
time.sleep(3)
driver.quit()
三、Pytest框架
1.总体介绍
1.1什么是框架
说明:
为解决一类事情的功能集合
1.2 什么是Pytest框架
是一个第三方单元测试框架,用它来做单元测试
1.3 为什么使用Pytest
- 能够组织多个用例去执行
- 提供丰富的断言方法
- 能够生成测试报告
2.基本使用
2.1 安装
pip install pytest==版本
2.2 断言
2.2.1什么是断言
让程序替人为判断测试程序执行结果是否符合预期结果的过程
2.2.2为什么学习断言
自动化脚本在执行的时候一般是无人值守状态,我们不知道执行 结果是否符合预期结果,所以我们需要让程序代替人为渐层程序的执行结果是否符合预期结果,这就需要使用断言
2.2.3 断言方法
assert XX #判断XX为真
assert not XX #判断XX不为真
assert a in b #判断b包含a
assert a == b #判断a等于b
assert a != b #判断a不等于b
2.3.4代码
def add(x, y):
return x + y
class TestPlus:
def test_a(self):
assert 2 == add(1, 1)
def test_b(self):
assert add(1, 1) == 2
def test_c(self):
assert 4 != add(1, 2)
def test_d(self):
assert 4 == add(1, 2)
3.setup和teardown
3.1应用场景
pytest在运行自动化脚本的前后会执行两个特殊的方法,分别是“前置”和“后置”方法
在执行脚本前会执行前置方法,在执行脚本后会执行后置方法
有了这两种方法,我们可以在前置方法中获取驱动对象,在后置方法中关闭驱动对象
3.2 概念和方法
1. 初始化(前置处理)
def setup_method(self)
2. 销毁(后置处理)
def teardown_method(self)
4. 运行与测试方法的始末,即:运行一次测试方法就会运行一次setup和teardown
3.3案例
import time
def add(x, y):
return x + y
class TestPlus:
def setup_method(self):
print("start-time", time.time())
def teardown_method(self):
print("end-time", time.time())
def test_a(self):
assert 2 == add(1, 1)
def test_b(self):
assert add(1, 1) == 2
def test_c(self):
assert 4 != add(1, 2)
def test_d(self):
assert 4 == add(1, 2)
4. 配置文件
4.1应用场景
使用配置文件,可以通过配置项来选择执行哪些目录下的哪些测试模块
4.2方法
步骤:
1. 项目下新建 scripts 模块
2. 将测试脚本文件放到 scripts 中
3. pytest 的配置文件放在自动化项目目录下
4. 名称为 pytest.ini
5. 第一行内容为 [pytest] , 后面写具体的配置参数
6. 命令行运行时会使用该配置文件中的配置
4.3实例
[pytest]
addopts = -s
testpaths = ./scripts
python_files = test_*.pypython_classes = Test*python_functions = test_*
addopts = -s
表示命令行参数
testpaths,python_files,python_classes,python_functions
表示执行 scripts 文件夹下的 test_ 开头 .py 结尾的文件下的 Test 开头的类下的 test_开头的函数
4.4注意点
1. 配置文件是否已经正确的加载?
- 通过控制台的 inifile 进行查看
2. windows中可能出现 “gbk” 错误?
- 删除 ini 文件中的所有中文
3. 在工作中这个文件也需要复制粘贴?
- 是的,一个项目只会用一次,只需理解,会修改即可
5.测试报告和插件
5.1 应用场景
自动化测试脚本最终执行时通过还是不通过,需要通过测试报告进行体现
5.2安装
使用命令 pip install pytest-html 进行安装
5.3 使用
在配置文件中的命令行参数中增加 --html=用户路径/report.html
步骤:
1. 命令行输入 pytest 运行脚本
2. 在项目目录下会对一个 report 文件夹,里面有个 report.html 即为测试报告
6.数据参数化
6.1应用场景
登录用户名和密码想测试多个值时,参数化可以使代码更整洁,可读性更高
6.2方法
# 数据参数化# 参数:
# argnames:参数名
# argvalues:参数对应值,类型必须为可迭代类型,一般使用list @pytest.mark.parametrize(argnames, argvalues)
6.3使用
在需要参数化的测试脚本之上加上装饰器 @pytest.mark.parametrize(argnames, argvalues)
6.4案例准备
# 要求:
# 不使用数据参数化的情况下, 写两个函数, 分别打印用户名 zhangsan 和 lisi
def test_a(self):
print("zhangsan")
def test_b(self):
print("lisi")
6.5单一参数
# 要求:
# 使用单一参数的数据参数化, 修改上面的代码
@pytest.mark.parametrize("name", ["zhangsan", "lisi"])def test_a(self, name):print(name)
6.6多个参数
import pytest
class TestDemo:
@pytest.mark.parametrize(("name", "password"), [("zhangsan", "12345"), ("lisi", "12345")])
def test_a(self, name, password):
print(name + "--" + password)
6.7推荐用法
import pytest
class TestDemo:
@pytest.mark.parametrize("dict", [{"name": "zhangsan", "password": "12345"}, {"name": "zhangsan", "password": "12345"}])
def test_a(self, dict):
print(dict)
print(dict["name"])
print(dict["password"])
四、PO模式
学习路线
V1: 不使用任何设计模式和单元测试框
V2: 使用 Pytest 管理用例
V3: 使用方法封装的思想, 对代码进行优化
V4: 采用PO模式的分层思想对代码进行拆分, 分离page
V5: 对PO分层之后的代码继续优化, 分离page中的元素和操作
V6: PO模式深入封装, 把共同操作提取封装
1.无模式
1.1 案例说明
对TPshop项目的登录模块进行自动化测试
提示:登录模块包含了很多测试用例,比如:账号不存在、密码错误、验证码错误、登录成功等等
为了节省时间我们只选取几个有代表性的用例来演示
1.2 选择测试用例
- 账号不存在
- 点击首页的 ‘登录’ 链接,进入登录页面
- 输入一个不存在的用户名
- 输入密码
- 输入验证码
- 点击登录按钮
- 获取错误提示信息
- 密码错误
- 点击首页的 ‘登录’ 链接,进入登录页面
- 输入用户名
- 输入一个错误的密码
- 输入验证码
- 点击登录按钮
- 获取错误提示信息
1.3 v1版本
1.3.1总体介绍
- 不使用任何设计模式和单元测试框架
- 每个文件里编写一个用例,完全的面向过程的编程方式
1.3.2示例代码
# 账号不存在
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 实例化驱动
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://192.168.10.139/")
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入一个不存在的用户名
driver.find_element(by=By.ID, value="username").send_keys("18800000000")
# 3. 输入密码
driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
# 关闭驱动
time.sleep(5)
driver.quit()
示例2
# 密码错误
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 实例化驱动
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://192.168.10.139/")
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入用户名
driver.find_element(by=By.ID, value="username").send_keys("19127038
@qq.com")
# 3. 输入密码
driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
# 关闭驱动
time.sleep(5)
driver.quit()
1.3.3 存在的问题
一条测试用例对应一个文件,用例较多时不方便管理维护
代码高度冗余
1.4.v2版本
1.4.1 总体介绍
引入Pytest管理用例, 并断言用例的执行结果
好处
方便组织、管理多个测试用例
提供了丰富的断言方法
方便生成测试报告
减少了代码冗余
1.4.2 示例代码
# 导包
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = webdriver.Chrome()
self.driver.maximize_window()
self.driver.implicitly_wait(10)
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
self.driver.quit()
# 定义用户不存在的测试方法
def test_login_account_not_exist(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入一个不存在的用户名
self.driver.find_element(by=By.ID, value="username").send_keys("18800000000")
# 3. 输入密码
self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
# 断言
assert "账号不存在!" ==msg
# 密码错误
def test_login_password_error(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入用户名
self.driver.find_element(by=By.ID, value="username").send_keys("19127038@qq.com")
# 3. 输入密码
self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
assert "密码错误!" == msg
2 方法封装
2.1 封装方法
概念
是将一些有共性的或多次被使用的代码提取到一个方法中,供其他地方调用
好处
- 避免代码冗余
- 容易维护
- 隐藏代码实现的细节
目的
用最少的代码实现最多的功能
使用方法封装的思想,对代码进行优化
2.2 v3版本
2.2.1 总体介绍
使用方法封装的思想,对代码进行优化
2.2.2 封装驱动
定义获取驱动对象的工具类
utils.py
from selenium import webdriver
class DriverUtils:
__driver = None
# 获取浏览器驱动
@classmethod
def get_driver(cls):
if cls.__driver is None:
cls.__driver = webdriver.Chrome()
cls.__driver.maximize_window()
cls.__driver.implicitly_wait(10)
return cls.__driver
# 关闭浏览器驱动
@classmethod
def quit_driver(cls):
if cls.__driver is not None:
cls.__driver.quit()
cls.__driver = None
test_login.py
# 导包
import time
from selenium.webdriver.common.by import By
from PO.v3.driver_untils import DriverUtils
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = DriverUtils.get_driver()
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
DriverUtils.quit_driver()
# 定义用户不存在的测试方法
def test_login_account_not_exist(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入一个不存在的用户名
self.driver.find_element(by=By.ID, value="username").send_keys("18800000000")
# 3. 输入密码
self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
# 断言
assert "账号不存在!" ==msg
# 密码错误
def test_login_password_error(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入用户名
self.driver.find_element(by=By.ID, value="username").send_keys("19127038@qq.com")
# 3. 输入密码
self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
print(msg)
assert "密码错误!" == msg
2.2.3 存在的问题
代码冗余
3.PO模式介绍
3.1思考
在做UI自动化时定位元素特别依赖页面,一旦页面发生变更就不得不跟着去修改定位元素的代码
存在的问题
如果开发人员修改了这个元素的id,这时候你就不得不修改所有对应的代码
存在大量冗余代码
3.2 PO模式概念
PO是Page Object的缩写,PO模式是自动化测试项目开发实践的最佳设计模式之一
核心思想:
- 通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化, 只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性
- 页面和测试脚本分离
3.3 PO模式分层
分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性
主要有以下几种分层方式
- 两层: 对象操作层+业务数据层
对象操作层: 封装页面信息, 包括元素以及对元素的操作
业务数据层: 封装多操作组合的业务以及测试数据
- 三层:对象库层+操作层+业务数据层
对象库层:封装定位元素的方法
操作层:封装对元素的操作
业务层:将一个或多个操作组合起来完成一个业务功能。比如登录:需要输入帐号、密码、点击登录三个操作
- 四层:对象库层+操作层+业务层+数据层
3.4 PO模式的优点
- 引入PO模式前
- 存在大量冗余代码
- 业务流程不清晰
- 后期维护成本大
- 引入PO模式后
- 减少冗余代码
- 业务代码和测试代码被分开,降低耦合性
- 维护成本低
4 PO模式实践
4.1 v4版本
4.1.1 总体介绍
采用PO模式的分层思想对代码进行拆分
4.1.2 PO封装
- 对登录页面进行封装
LoginPage
- 编写测试用例
TestLogin
4.1.3 示例代码
结构
- utils包
-- utils.py
- page包
-- login_page.py
- scripts包
-- test_login.py
- pytest.ini
login_page.py
# 定义测试类
from selenium.webdriver.common.by import By
class LoginPage:
def __init__(self, driver):
self.driver = driver
def click_login_link(self):
# 点击首页的 ‘登录’ 链接,进入登录页面
return self.driver.find_element(by=By.CLASS_NAME, value="red").click()
def input_username(self, username):
# 输入用户名
return self.driver.find_element(by=By.ID, value="username").send_keys(username)
def input_password(self, password):
# 输入密码
return self.driver.find_element(by=By.ID, value="password").send_keys(password)
def input_verify_code(self, code):
# 输入验证码
return self.driver.find_element(by=By.ID, value="verify_code").send_keys(code)
def click_login_btn(self):
# 点击登录按钮
return self.driver.find_element(by=By.NAME, value="sbtbutton").click()
def get_msg(self):
msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
return msg
test_login.py
# 导包
import time
from PO.v4.page.login_page_1 import LoginPage
from PO.v4.utils.driver_untils import DriverUtils
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = DriverUtils.get_driver()
self.login_page = LoginPage(self.driver)
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
DriverUtils.quit_driver()
# 定义用户不存在的测试方法
def test_login_account_not_exist(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.login_page.click_login_link()
# self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入一个不存在的用户名
self.login_page.input_username("18800000000")
# self.driver.find_element(by=By.ID, value="username").send_keys("18800000000")
# 3. 输入密码
self.login_page.input_password("123456")
# self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.login_page.input_verify_code("8888")
# self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.login_page.click_login_btn()
# self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
# msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
# print(msg)
# 断言
assert "账号不存在!" == self.login_page.get_msg()
# 密码错误
def test_login_password_error(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.login_page.click_login_link()
# 2. 输入用户名
self.login_page.input_username("19127038@qq.com")
# 3. 输入密码
self.login_page.input_password("123456")
# 4. 输入验证码
self.login_page.input_verify_code("8888")
# 5. 点击登录按钮
self.login_page.click_login_btn()
# 6. 获取错误提示信息
assert "密码错误!" == self.login_page.get_msg()
driver_untils.py
from selenium import webdriver
class DriverUtils:
__driver = None
# 获取浏览器驱动
@classmethod
def get_driver(cls):
if cls.__driver is None:
cls.__driver = webdriver.Chrome()
cls.__driver.maximize_window()
cls.__driver.implicitly_wait(10)
return cls.__driver
# 关闭浏览器驱动
@classmethod
def quit_driver(cls):
if cls.__driver is not None:
cls.__driver.quit()
cls.__driver = None
4.2 v5版本
4.2.1 总体介绍
对PO分层后的代码继续优化
4.2.2优化内容
- 分离page页面中的元素和操作
- 优化元素定位方式
4.2.3 示例代码
只变更了login_page.py
# 定义测试类
from selenium.webdriver.common.by import By
class LoginPage:
# 登录链接 按钮
login_link_btn = By.CLASS_NAME, "red"
# 用户名 输入框
username_input = By.ID, "username"
# 密码 输入框
password_input = By.ID, "password"
# 验证码 输入框
verify_code_input = By.ID, "verify_code"
# 登录 按钮
login_btn = By.NAME, "sbtbutton"
# 提示信息
msg_info = By.CSS_SELECTOR, ".layui-layer-content"
def __init__(self, driver):
self.driver = driver
def find_el(self, feature):
return self.driver.find_element(*feature)
def click_login_link(self):
# 点击首页的 ‘登录’ 链接,进入登录页面
return self.find_el(self.login_link_btn).click()
def input_username(self, username):
# 输入用户名
return self.find_el(self.username_input).send_keys(username)
def input_password(self, password):
# 输入密码
return self.find_el(self.password_input).send_keys(password)
def input_verify_code(self, code):
# 输入验证码
return self.find_el(self.verify_code_input).send_keys(code)
def click_login_btn(self):
# 点击登录按钮
return self.find_el(self.login_btn).click()
def get_msg(self):
msg = self.find_el(self.msg_info).text
return msg
5. PO模式深入封装
5.1 v6版本
5.1.1 总体介绍
把共同的方法进行封装
5.1.2 优化内容
- 封装操作基类
- 封装查找元素的方法
- 封装基本操作方法: 点击/ 清空/ 输入 等
- page继承操作类
5.1.3示例代码
结构
结构
- base
-- base_action.py
- utils
-- utils.py
- page
-- login_page.py
- scripts
-- test_login.py
- pytest.ini
base_action.py
class BaseAction:
def __init__(self, driver):
self.driver = driver
def find_el(self, feature):
return self.driver.find_element(*feature)
def find_els(self, feature):
return self.driver.find_elements(*feature)
def click(self, feature):
self.find_el(feature).click()
def input(self, feature, context):
self.find_el(feature).send_keys(context)
def clear(self, feature):
return self.find_el(feature).clear()
** login_page.py**
# 定义测试类
from selenium.webdriver.common.by import By
from PO.v6.base.base_action import BaseAction
class LoginPage(BaseAction):
# 登录链接 按钮
login_link_btn = By.CLASS_NAME, "red"
# 用户名 输入框
username_input = By.ID, "username"
# 密码 输入框
password_input = By.ID, "password"
# 验证码 输入框
verify_code_input = By.ID, "verify_code"
# 登录 按钮
login_btn = By.NAME, "sbtbutton"
# 提示信息
msg_info = By.CSS_SELECTOR, ".layui-layer-content"
def click_login_link(self):
# 点击首页的 ‘登录’ 链接,进入登录页面
return self.click(self.login_link_btn)
def input_username(self, context):
# 输入用户名
return self.input(self.username_input, context)
def input_password(self, context):
# 输入密码
return self.input(self.password_input, context)
def input_verify_code(self, context):
# 输入验证码
return self.input(self.verify_code_input, context)
def click_login_btn(self):
# 点击登录按钮
return self.click(self.login_btn)
def get_msg(self):
return self.find_el(self.msg_info).text
test_login.py
# 导包
import time
from PO.v6.page.login_page_1 import LoginPage
from PO.v6.utils.driver_untils import DriverUtils
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = DriverUtils.get_driver()
self.login_page = LoginPage(self.driver)
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
DriverUtils.quit_driver()
# 定义用户不存在的测试方法
def test_login_account_not_exist(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.login_page.click_login_link()
# self.driver.find_element(by=By.CLASS_NAME, value="red").click()
# 2. 输入一个不存在的用户名
self.login_page.input_username("18800000000")
# self.driver.find_element(by=By.ID, value="username").send_keys("18800000000")
# 3. 输入密码
self.login_page.input_password("123456")
# self.driver.find_element(by=By.ID, value="password").send_keys("123456")
# 4. 输入验证码
self.login_page.input_verify_code("8888")
# self.driver.find_element(by=By.ID, value="verify_code").send_keys("8888")
# 5. 点击登录按钮
self.login_page.click_login_btn()
# self.driver.find_element(by=By.NAME, value="sbtbutton").click()
# 6. 获取错误提示信息
# msg = self.driver.find_element(by=By.CSS_SELECTOR, value=".layui-layer-content").text
# print(msg)
# 断言
assert "账号不存在!" == self.login_page.get_msg()
# 密码错误
def test_login_password_error(self):
# 1. 点击首页的 ‘登录’ 链接,进入登录页面
self.login_page.click_login_link()
# 2. 输入用户名
self.login_page.input_username("19127038@qq.com")
# 3. 输入密码
self.login_page.input_password("123456")
# 4. 输入验证码
self.login_page.input_verify_code("8888")
# 5. 点击登录按钮
self.login_page.click_login_btn()
# 6. 获取错误提示信息
assert "密码错误!" == self.login_page.get_msg()
五、数据驱动
1 数据驱动介绍
1.1 数据驱动概念
数据驱动: 是以数据来驱动整个测试用例的执行,也就是测试数据决定测试结果
比如我们要测试加法,我们的测试数据是1和1,测试结果就是2,如果测试数据是1和2,测试结果就是3
1.2 数据驱动特点
- 可以把数据驱动理解为一种模式或者一种思想
- 数据驱动技术可以将用户把关注点放在对测试数据的构建和维护上,而不是直接维护脚本,可以利用同样的过程对不同的数据输入进行测试
- 数据驱动的实现要依赖参数化的技术
1.3 数据来源
- 直接定义在测试脚本中(简单直观,但代码和数据未实现真正的分离,不方便后期维护)
- 从文件读取数据,如JSON、excel、xml、txt等格式文件
- 从数据库中读取数据
2 JSON操作
2.1 JSON基本介绍
2.1.1 JSON概念
JSON的全称是”JavaScript Object Notation”,是JavaScript对象表示法,它是一种基于文本,独立于语言的轻量级数据交换格式
2.1.2 JSON特点
特点
- JSON是纯文本
- JSON具有良好的自我描述性,便于阅读和编写
- JSON具有清晰的层级结构
- 有效地提升网络传输效率
2.1.3 JSON语法规则
- 大括号保存对象
- 中括号保存数组
- 对象数组可以相互嵌套
- 数据采用键值对表示
- 多个数据由逗号分隔
2.1.4 JSON值
- 数字(整数或浮点数)
- 字符串(在双引号中)
- 逻辑值(true 或 false)
- 数组(在中括号中)
- 对象(在大括号中)
- null
2.2 JSON基本操作
2.2.1 总体介绍
操作内容
- python字典与JSON之间的转换
- JSON文件读写
在python中想要操作 JSON, 需要先导入依赖包
import json
把python字典类型转换为JSON字符串
2.2.2 字典与JSON转换
# 定义一个字典对象dict_str = {
"name":"zhangsan",
"age":18,
"is_man": False,
"school": None
}
# python字典转换成json格式数据
json_str = json.dumps(dict_str)
把JSON字符串转换为python字典
# 定义一个JSON字符串
json_str = '{"name": "zhangsan", "age": 18, "is_man": false, "school": null}'
# json格式数据转换成python字典
json.loads(json_str)
2.2.3 JSON文件读写
准备数据
{
"name": "tom",
"age": 18,
"isMan": true,
"school": null,
"address": {
"country": "中国",
"city": "广州"
},
"numbers": [2, 4, 6, 8],
"like_links": [
{
"name": "Baidu",
"url": "http://www.baidu.com"
},
{
"name": "TaoBao",
"url": "http://www.taobao.com"
}
]
}
读取JSON文件+写入JSON文件
import json
# 读取文件
with open("data.json", "r", encoding="utf-8") as f:
data1 = json.load(f)
print(data1)
# 把字典写入文件 data2.json
data2 = data1
with open("data2.json", "w", encoding="utf-8") as f:
json.dump(data2, f)
# 把字典写入文件 data3.json 解决中文乱码
data3 = data1
with open("data3.json", "w", encoding="utf-8") as f:
json.dump(data3, f, ensure_ascii=False)
3 数据驱动实战一
3.1 案例介绍
对tpshop项目的登录模块进行自动化测试, 需要使用数据驱动
为v6版本增加数据驱动
3.2 实现步骤
- 编写测试用例
- 采用PO模式的分层思想对页面进行封装
- 编写测试脚本
- 定义数据文件, 实现参数化
3.3 用例设计
3.4测试脚本参数化
核心代码(login_page)
# 导包
import time
import pytest
from v7.page.login_page_1 import LoginPage
from v7.utils.driver_untils import DriverUtils
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = DriverUtils.get_driver()
self.login_page = LoginPage(self.driver)
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
DriverUtils.quit_driver()
# 定义测试登录的方法,实现脚本参数化
dict1 = {"username": "18800000000", "password": "123456", "code": "8888", "msg": "账号不存在!"}
dict2 = {"username": "19127038@qq.com", "password": "123456", "code": "8888", "msg": "密码错误!"}
@pytest.mark.parametrize("params", [dict1, dict2])
def test_login(self,params):
self.login_page.click_login_link()
self.login_page.input_username(params["username"])
self.login_page.input_password(params["password"])
self.login_page.input_verify_code(params["code"])
self.login_page.click_login_btn()
assert params["msg"] == self.login_page.get_msg()
3.5数据文件
新建 ./data/login_data.json 文件
{
"login_001_account_not_exist":{
"username":"1880000000",
"password": "123456",
"code": "8888",
"msg": "账号不存在!"
},
"login_002_password_error":{
"username":"1912703877@qq.com",
"password": "123456",
"code": "8888",
"msg": "密码错误!"
}
}
3.6数据解析函数
需要对JSON数据文件进行解析,在test_login.py中,学一个解析函数
def analyze_data(filename):
with open("../v8/data/login_data.json", "r", encoding="utf-8") as f:
list_data = []
dict_data = json.load(f)
for value in dict_data.values():
list_data.append(value)
return list_data
3.7 抽离解析函数
在base包中新建base_analyze.py文件
import json
def analyze_data(filename):
with open("../v8/data/" + filename, "r", encoding="utf-8") as f:
list_data = []
dict_data = json.load(f)
for value in dict_data.values():
list_data.append(value)
return list_data
在test_login.py中进行调用, 核心代码如下
# 导包
import time
import pytest
from v8.base.base_analyz import analyze_data
from v8.page.login_page_1 import LoginPage
from v8.utils.driver_untils import DriverUtils
# 定义测试类
class TestLogin:
def setup_method(self):
self.driver = DriverUtils.get_driver()
self.login_page = LoginPage(self.driver)
self.driver.get("http://192.168.10.139/")
def teardown_method(self):
time.sleep(5)
DriverUtils.quit_driver()
@pytest.mark.parametrize("params", analyze_data("login_data.json"))
def test_login(self, params):
self.login_page.click_login_link()
self.login_page.input_username(params["username"])
self.login_page.input_password(params["password"])
self.login_page.input_verify_code(params["code"])
self.login_page.click_login_btn()
assert params["msg"] == self.login_page.get_msg()
4 数据驱动实战二
4.1 案例介绍
对网页计算器,进行加法的测试操作。通过读取数据文件中的数据来执行用例
网址:http://cal.apple886.com/
4.2 实现步骤
- 编写测试用例
- 采用PO模式的分层思想对页面进行封装
- 编写测试脚本
- 定义数据文件, 实现参数化