Web自动化测试

课程大纲

序号 章节 知识点
1 第一章 Web自动化入门 1.认识自动化及自动化测试
2.自动化测试工具选择
3.环境搭建
2 第二章 Selenium-API 1.元素定位方式
2.元素和浏览器的操作方法
3.鼠标和键盘操作
4.元素等待
5.HTML特殊元素处理
6.窗口截图
7.验证码处理
3 第三章 Pytest框架 1.Pytest基本使用
4 第四章 PO模式 1.方法封装
2.PO模式介绍
3.PO模式实战
5 第五章 数据驱动 1.JSON读写
2.数据驱动介绍
3.数据驱动实战
6 第六章 日志收集 1.日志相关概念
2.日志的基本方法
3.日志的高级方法

第一章 Web自动化入门

1 自动化

1.1 概念

由机器设备代替人工自动完成指定目标的过程

1.2 优点

  • 减少人工劳动力

  • 提高工作效率

  • 产品规格统一标准

  • 规模化(批量生产)

  • 安全

2 自动化测试

2.1 概念

用程序代替人工去执行测试的过程

软件测试: 对程序进行操作,以发现程序错误,并验证其是否满足需求的过程

2.2 应用场景

  • 解决-回归测试

    回归测试:项目在发新版本之后对项目之前的功能进行验证

  • 解决-压力测试

    压力测试:可以理解多用户同时去操作软件,统计软件服务器处理多用户请求的能力

  • 解决-兼容性测试

    兼容性测试:不同浏览器(IE、Firefox、Chrome)等等,执行的用例都是同一条,只是浏览器不同

  • 解决-冒烟测试

    冒烟测试:针对最基本的功能或者流程进行的测试

  • 提高测试效率,保证产品质量

2.3 正确认识(存在即合理)

优点

  • 较少的时间内运行更多的测试用例
  • 自动化脚本可重复运行
  • 减少人为的错误
  • 克服手工测试的局限性

误区

  • 自动化测试可以完全替代手工测试
  • 自动化测试一定比手工测试厉害
  • 自动化测试可以发掘更多的BUG (自动化脚本执行的用例一般都是重复的,操作都是一致的)
  • 自动化测试适用于所有功能

2.4 分类

  • Web-自动化测试 (本阶段学习)
  • 移动-自动化测试
  • 接口-自动化测试
  • 桌面自动化 (桌面层次的自动化)
  • 单元测试-自动化测试
  • web自动化/移动自动化测试都属于UI自动化

3 Web自动化测试

概念

用程序代替人工去执行Web测试的过程

什么是接口自动化测试?

什么是移动自动化测试?

什么样的项目适合做Web自动化测试

  • 需求变动不频繁 (如果需求变动频繁,脚本代码需要变动,将会浪费大量时间精力)
  • 项目周期长 (如果周期过短,整个测试流程的时间有限,优先完成功能测试,编写脚本太费时间)
  • 项目需要回归测试

Web自动化测试什么时候开始

手工测试结束后 (为什么是手工测试结束?)

web自动化测试的场景多为回归测试

Web自动化测试所属分类

属于功能测试 (黑盒)

我们编写了自动化的脚本进行测试为什么不属于白盒或者灰盒?

我们编写的自动化脚本本身不属于程序内部的代码

4 Selenium

4.1 Web自动化工具简介

主流工具

  • QTP

    一个商业化的功能测试工具, 收费, 支持web/ 桌面自动化测试

  • Selenium(重点)

    一个开源的web自动化测试工具, 主要做功能测试(开源)

  • Robot framework

    一个基于python可扩展的关键字驱动的自动化测试框架(运用较少)

    什么是关键字驱动?

    什么是驱动?

Selenium特点

  • 开源软件:源代码开放可以根据需要来增加工具的某些功能
  • 跨平台:linux、windows、mac
  • 支持多种浏览器:Firefox、Chrome、IE、Edge、Opera、Safari等
  • 支持多种语言:Python、Java、C#、JavaScript、Ruby、PHP等
  • 成熟稳定:目前已经被google、百度、腾讯等公司广泛使用
  • 功能强大:能够实现类似商业工具的大部分功能,因为开源性,可实现定制化功能(二次开发)

4.2 环境搭建

4.2.1 搭建步骤

基于python搭建环境:

  1. python 开发环境 (python v3.x)

  2. 安装 selenium 包

  3. 安装浏览器

  4. 安装浏览器驱动

    保证能够用程序驱动浏览器, 实现自动化测试

为什么我们是基于python搭建的,能基于其他语言进行环境搭建吗?

4.2.2 安装selenium包

安装

pip install selenium

验证

pip list
# 或者
pip show selenium

卸载

pip uninstall selenium

离线安装

  • 把下载好的selenium包放到 D:\python\Lib\site-packages\ 下
  • 命令行, 进入到~\python\Lib\site-packages\selenium-3.141.0
  • 执行 python setup.py install
4.2.3 安装浏览器驱动

驱动下载

  • Chrome

    下载地址: http://chromedriver.storage.googleapis.com/index.html

    镜像地址: http://npm.taobao.org/mirrors/chromedriver/

    浏览器下载地址: https://www.chromedownloads.net/

  • Firefox

    下载地址: https://github.com/mozilla/geckodriver/releases

  • Edge(ie浏览器的改进版)

    下载地址: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/

安装步骤

  1. 下载浏览器驱动

    各个驱动下载地址: http://www.seleniumhq.org/download/

    浏览器的版本和驱动版本要一致

  2. 把驱动文件所在目录添加到Path环境变量中

    或者直接放到Python安装目录,因为Python已添加到Path中

4.3 入门示例

4.3.1 需求
通过程序启动浏览器,并打开百度首页,暂停3秒,关闭浏览器
4.3.2 实现步骤
1.导包
2.创建浏览器驱动对象
3.打开百度首页
4.暂停3秒
5.关闭驱动对象
4.3.3 示例代码
# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.打开百度首页
driver.get("http://www.baidu.com")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()

第二章 Selenium-API

1 元素定位基础

1.1 为什么进行元素定位

让程序操作指定元素,就必须先找到此元素

什么是元素?

HTML网页实际上就是由许多的HTML元素构成的文本文件,并且任何网页浏览器都可以直接运行HTML文件。所以可以这样说,HTML元素就是构成HTML文件的基本对象,HTML元素可以说是一个统称而已。HTML元素就是通过使用HTML标签进行定义的。

什么是标签?

标签就是、、

等被尖括号“<”和“>”包起来的对象,绝大部分的标签都是成对出现的,如 、。

标签就是用来标记HTML元素的。位于起始标签和结束标签之间的文本就是HTML元素的内容。

什么是属性?

为HTML元素提供各种附加信息的就是HTML属性,它总是以"属性名=属性值"这种名值对的形式出现,而且属性总是在HTML元素的开始标签中进行定义。

1.2 如何进行元素定位

元素定位就是通过元素的信息或元素层级结构来定位元素的

html页面由标签构成,标签的基本格式如下:<标签名 属性名1="属性值1" 属性名2="属性值2">文本</标签名>
示例:<input id="username" type="text" name="username" placeholder="用户名" /><div id="my_cart"><span>我的购物车</span></div>

思考:如何快速的查看一个元素的相关信息?

1.3 浏览器开发者工具

概念

浏览器开发者工具就是给专业的web应用和网站开发人员使用的工具。包含了对HTML查看和编辑、Javascript控制台、网络状况监视等功能,是开发JavaScript、CSS、HTML和Ajax的得力助手

作用

定位元素, 查看元素信息

使用

需求:

使用浏览器开发者工具, 查看百度输入框的相关信息

  • 安装: 无需安装
  • 启动: 快捷键一般都是F12
  • 使用方式
    • 方式一: 在要查看的元素上点击右键选择 ‘查看元素’(Firefox) 或者 ‘检查’(Chrome)
    • 方式二: 先打开浏览器开发者工具, 点击选择元素的图标, 移动鼠标到要查看的元素, 然后点击

1.4 元素定位方式

1.4.1 总体介绍
Selenium提供了八种定位元素方式
1. id
2. name
3. class_name
4. tag_name
5. link_text
6. partial_link_text
7. XPath
8. CSSid、name、class_name:为元素属性定位
tag_name:为元素标签名称
link_text、partial_link_text:为超链接定位(a标签)
XPath:为元素路径定位
CSS:为CSS选择器定位
1.4.2 id定位
说明:id定位就是通过元素的id属性来定位元素,HTML规定id属性在整个HTML文档中必须是唯一的;
前提:元素有id属性
但是id唯一不是硬性要求,一般情况id都是唯一的

方法

element = driver.find_element_by_id(id)

案例

案例演示环境说明:
受限于网络速度的影响,我们案例采用本地的html页面来演示。这样可以提高学习效率和脚本执行速率需求:打开注册A.html页面,完成以下操作
1.使用id定位,输入用户名:admin
2.使用id定位,输入密码:123456
3.3秒后关闭浏览器窗口
实现步骤分析:
1. 导入selenium包 --> from selenium import webdriver
2. 导入time包 --> import time
3. 实例化浏览器驱动对象 --> driver = webdriver.Firefox()
4. 打开注册A.html --> driver.get(url)
5. 调用id定位方法 --> element = driver.find_element_by_id("")
6. 使用send_keys()方法输入内容 --> element.send_keys("admin")
7. 暂停3秒 --> time.sleep(3)
8. 关闭浏览器驱动对象 --> driver.quit()说明:为了更好的学习体验,我们先暂时使用下send_keys()方法来输入内容

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_id("userA").send_keys("admin")
driver.find_element_by_id("passwordA").send_keys("123456")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()
1.4.3 name定位
说明:name定位就是根据元素name属性来定位。HTML文档中name的属性值是可以重复的。
前提:元素有name属性

方法

element = driver.find_element_by_name(name)

案例

需求:打开注册A.html页面,完成以下操作
1.使用name定位,输入用户名:admin
2.使用name定位,输入密码:123456
3.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_name("userA").send_keys("admin")
driver.find_element_by_name("passwordA").send_keys("123456")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()
1.4.4 class_name定位
说明:class_name定位就是根据元素class属性值来定位元素。HTML通过使用class来定义元素的样式
前提:元素有class属性
注意:如果class有多个属性值,只能使用其中的一个

方法

element = driver.find_element_by_class_name(class_name)

案例

需求:打开注册A.html页面,完成以下操作
1.通过class_name定位电话号码A,并输入:13122223333
2.通过class_name定位电子邮箱A,并输入:123456@qq.com
3.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_class_name("telA").send_keys("13122223333")
driver.find_element_by_class_name("emailA").send_keys("123456@qq.com")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()
1.4.5 tag_name定位
说明:tag_name定位就是通过标签名来定位;
HTML本质就是由不同的tag组成,每一种标签一般在页面中会存在多个,所以不方便进行精确定位,一般很少使用

方法

element = driver.find_element_by_tag_name(tag_name)
# 如果存在多个相同标签,则返回符合条件的第一个标签
# 如何获取第二个元素?后面会讲到

案例

需求:打开注册A.html页面,完成以下操作
1.使用tag_name定位用户名输入框,并输入:admin
2.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_tag_name("input").send_keys("admin")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()
1.4.6 link_text定位
说明:link_text定位是专门用来定位超链接元素(<a>标签</a>),并且是通过超链接的文本内容来定位元素

方法

element = driver.find_element_by_link_text(link_text)
# link_text:为超链接的全部文本内容

案例

需求:打开注册A.html页面,完成以下操作
1.使用link_text定位(访问 新浪 网站)超链接,并点击
2.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_link_text("访问 新浪 网站").click()# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()
1.4.7 partial_link_text定位
说明:partial_link_text定位是对link_text定位的补充,link_text使用全部文本内容匹配元素,而partial_link_text可以使用局部来匹配元素,也可以使用全部文本内容匹配元素

方法

element = driver.find_element_by_partial_link_text(partial_link_text)
# partial_link_text:可以传入a标签局部文本-能表达唯一性

案例

需求:打开注册A.html页面,完成以下操作
1.使用partial_link_text定位(访问 新浪 网站)超链接,并点击
2.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
driver.find_element_by_partial_link_text("访问").click()# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()

1.5 定位一组元素

使用上述方法的时候, 发现有一些类似的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TRn7ywk1-1609329063534)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201222143040129.png)]

方法

elements = driver.find_elements_by_xxx("xxxxxx")
作用:1. 查找定位所有符合条件的元素2. 返回值是一个列表
说明:列表数据格式的读取需要指定下标(下标从0开始)
思考:如果定位一组列表没定位到,会报错吗?

案例

需求:打开注册A.html页面,完成以下操作
1.使用tag_name定位密码输入框(第二个input标签),并输入:123456
2.3秒后关闭浏览器窗口

代码:

# 1.导包
import time
from selenium import webdriver# 2.创建浏览器驱动对象
driver = webdriver.Chrome()# 3.业务操作
driver.get("C:\\Users\\Administrator.USER-20190219YL\\Desktop\\pagetest\\注册A.html")
#elements = driver.find_elements_by_tag_name("input")
#elements[1].send_keys("123456")
driver.find_elements_by_tag_name("input")[1].send_keys("123456")# 4.暂停3秒
time.sleep(3)# 5.关闭驱动对象
driver.quit()

2 元素定位-XPath/CSS

2.1 总体介绍

为什么要学习XPath/CSS定位

1. 如果要定位的元素没有id、name、class属性,该如何进行定位?
2. 如果通过name、class、tag_name无法定位到唯一的元素,该如何进行定位?
示例:<input type="submit" value="提交" />

2.2 XPath定位

2.2.1 什么是XPath

跟之前学接口测试jmeter中的xpath提取器有什么区别?

1. XPath即为XML Path的简称,它是一门在 XML 文档中查找元素信息的语言
2. HTML可以看做是XML的一种实现,所以Selenium用户可以使用这种强大的语言在Web应用中定位元素XML:一种标记语言,用于数据的存储和传递。 后缀.xml结尾
<?xml version="1.0" encoding="UTF-8" ?>
<node><db id="db" desc="三条边的长度都一样"><b1>3</b1><b2>3</b2><b3>3</b3><expect>等边三角形</expect></db><dy><b1>4</b1><b2>4</b2><b3>5</b3><expect>等腰三角形</expect></dy>
</node>

XPath之所以强大, 是因为它有非常灵活的定位方式

2.2.2 XPath定位方式
2.2.2.1 总体介绍

四种定位方式

1. 路径-定位
2. 利用元素属性-定位
3. 属性与逻辑结合-定位
4. 层级与属性结合-定位

方法

element = driver.find_element_by_xpath(xpath)
2.2.2.2 路径

绝对路径:从最外层元素到指定元素之间所有经过元素层级的路径

  1. 绝对路径以/html根节点开始,使用/来分隔元素层级
    如:/html/body/div/fieldset/p[1]/input
  2. 绝对路径对页面结构要求比较严格,不建议使用

相对路径:匹配任意层级的元素,不限制元素的位置

  1. 相对路径以//开始

  2. 格式://input 或者 //*

    如://input[@id=‘passwordA’] #找input标签,并且查找id是passwordA的元素

    如果是//*input[@id=‘passwordA’] #找所有的标签,并且查找id是passwordA的元素

练习

需求:打开注册A.html页面,完成以下操作
1.使用绝对路径定位用户名输入框,并输入:admin
2.暂停2秒
3.使用相对路径定位密码输入框,并输入:123

核心代码:

driver.find_element_by_xpath("/html/body/div/fieldset/form/p[1]/input").send_keys("admin")
time.sleep(2)
driver.find_element_by_xpath("//*[@id='passwordA']").send_keys("123")
2.2.2.3 利用元素属性
说明:通过使用元素的属性信息来定位元素
格式://input[@id='userA'] 或者 //*[@id='userA']
//input[@id='userA'] 是你知道他的层级是input标签,而且你也知道他的id是userA
//*[@id='userA'] 是你不知道他是哪个层级的标签,*代表全部,但是你知道他的id是userA

练习

需求:打开注册A.html页面,完成以下操作
利用元素的属性信息精确定位用户名输入框,并输入:admin
2.2.2.4 属性与逻辑结合
说明:解决元素之间个相同属性重名问题
格式://*[@name='tel' and @class='tel']

练习

需求:打开注册A.html页面,完成以下操作
使用属性与逻辑结合定位策略,在密码对应的输入框里输入:123

核心代码:

driver.find_element_by_xpath("//*[@type='password' and @id='passwordA']").send_keys("123")
2.2.2.5 层级与属性结合
说明:如果通过元素自身的信息不方便直接定位到该元素,则可以先定位到其父级元素,然后再找到该元素
格式://*[@id='p1']/input

练习

需求:打开注册A.html页面,完成以下操作
使用层级与属性结合定位策略,在test1对应的输入框里输入:test1

核心代码:

driver.find_element_by_xpath("//*[@id='p1']/input").send_keys("test1")
2.2.2.6 XPath扩展
//*[text()="xxx"] 文本内容是xxx的元素
//*[contains(@attribute,'xxx')] 属性中含有xxx的元素    #attribute是属性名
//*[starts-with(@attribute,'xxx')] 属性以xxx开头的元素

举例:

driver.find_element_by_xpath("//*[text()='访问 新浪 网站']").click()
driver.find_element_by_xpath("//*[contains(@placeholder,'用户名')]").send_keys('admin')
driver.find_element_by_xpath("//*[starts-with(@placeholder,'请输入密')]").send_keys('123')

2.3 CSS定位

前端三件宝:html,js,css

html管理布局,js做效果,css做样式

如:html告诉你一个人应该有什么器官,js告诉你一个人能做哪些行为动作,css表示一个人长什么样

如:你想去定义一只手长啥样,你需要先定位到哪只手,才能去定义他的样式

css选择器,是css提供的一种定位方式。

2.3.1 什么是CSS
1. CSS(Cascading Style Sheets)是一种语言,它用来描述HTML元素的显示样式
2. 在CSS中,选择器是一种模式,用于选择需要添加样式的元素
3. 在Selenium中也可以使用这种选择器来定位元素提示:
1. 在selenium中推荐使用CSS定位,因为它比XPath定位速度要快
2. css选择器语法非常强大,在这里我们只学习在测试中常用的几个
2.3.2 CSS定位方式
2.3.2.1 总体介绍

常用定位方式

1. id选择器
2. class选择器
3. 元素选择器
4. 属性选择器
5. 层级选择器

方法

element = driver.find_element_by_css_selector(css_selector)
2.3.2.2 id选择器
说明:根据元素id属性来选择
格式:#id
例如:#userA <选择id属性值为userA的元素>

练习

需求:打开注册A.html页面,完成以下操作
使用CSS定位方式中id选择器定位用户名输入框,并输入:admin

核心代码:

driver.find_element_by_css_selector("#userA").send_keys("admin")
2.3.2.3 class选择器
说明:根据元素class属性来选择
格式:.class
例如:.telA <选择class属性值为telA的所有元素>

练习

需求:打开注册A.html页面,完成以下操作
使用CSS定位方式中class选择器定位电话号码输入框,并输入:13100000000

核心代码:

driver.find_element_by_css_selector(".telA").send_keys("123456")
2.3.2.4 元素选择器
说明:根据元素的标签名选择
格式:element
例如:input <选择所有input元素>

练习

需求:打开注册A.html页面,完成以下操作
使用CSS定位方式中元素选择器定位注册按钮,并点击

核心代码:

driver.find_element_by_css_selector("button").click()
2.3.2.5 属性选择器
说明:根据元素的属性名和值来选择
格式:[attribute=value] element[attribute=value]
例如:[type="password"] <选择type属性值为password的元素>

练习

需求:打开注册A.html页面,完成以下操作
使用CSS定位方式中属性选择器定位密码输入框,并输入:123456

核心代码:

driver.find_element_by_css_selector("[type='password']").send_keys("123456")
2.3.2.6 层级选择器
说明:根据元素的父子关系来选择
格式1:element1>element2 通过element1来定位element2,并且element2必须为element1的直接子元素
例如1:p[id='p1']>input <定位指定p元素下的直接子元素input>
格式2:element1 element2 通过element1来定位element2,并且element2为element1的后代元素
例如2:p[id='p1'] input <定位指定p元素下的后代元素input>

练习

需求:打开注册A.html页面,完成以下操作
使用CSS定位方式中的层级选择器定位用户名输入框,并输入:admin

核心代码:

#driver.find_element_by_css_selector("p[id='pa']>input").send_keys("admin")
driver.find_element_by_css_selector("div[class='zc'] input").send_keys("admin")
2.3.2.7 CSS扩展
input[type^='p'] type属性以p字母开头的元素
input[type$='d'] type属性以d字母结束的元素
input[type*='w'] type属性包含w字母的元素

举例:

# 输入用户名
# driver.find_element_by_css_selector("input[type^='t']").send_keys("admin")
driver.find_element_by_css_selector("input[id^='u']").send_keys("admin")
driver.find_element_by_css_selector("input[type$='t']").send_keys("admin")
driver.find_element_by_css_selector("input[type*='ex']").send_keys("admin")

2.4 XPath和CSS对比

定位方式 XPath CSS
元素名 //input input
id //*[@id=‘userA’] #userA
class //*[@class=‘telA’] .telA
属性 1. //[starts-with(@attribute,‘x’)]
2. //
[text()=“x”]
3. //*[contains(@attribute,‘x’)]
1. input[type^=‘x’]
2. input[type$=‘x’]
3. input[type*=‘x’]

3 元素定位总结

3.1 元素定位分类汇总

1. id、name、class_name:为元素属性定位
2. tag_name:为元素标签名称
3. link_text、partial_link_text:为超链接定位(a标签)
4. XPath:为元素路径定位
5. CSS:为CSS选择器定位

3.2 另一种写法

3.2.1 方法介绍

方法

方法:find_element(by=By.ID, value=None)
备注:需要两个参数,第一个参数为定位的类型由By提供,第二个参数为定位的具体方式
导包:from selenium.webdriver.common.by import By

示例

1. driver.find_element(By.CSS_SELECTOR, '#emailA').send_keys("123@126.com")
2. driver.find_element(By.XPATH, '//*[@id="emailA"]').send_keys('1234@qq.com')
3. driver.find_element(By.ID, "userA").send_keys("admin")
4. driver.find_element(By.NAME, "passwordA").send_keys("123456")
5. driver.find_element(By.CLASS_NAME, "telA").send_keys("13111111111")
6. driver.find_element(By.TAG_NAME, 'input').send_keys("123")
7. driver.find_element(By.LINK_TEXT, '访问 新浪 网站').click()
8. driver.find_element(By.PARTIAL_LINK_TEXT, '访问').click()
3.2.2 区别
    def find_element_by_id(self, id_):"""Finds an element by id.:Args:- id\_ - The id of the element to be found.:Returns:- WebElement - the element if it was found:Raises:- NoSuchElementException - if the element wasn't found:Usage:element = driver.find_element_by_id('foo')"""return self.find_element(by=By.ID, value=id_)

4 元素操作

4.1 应用场景

1. 需要让脚本模拟用户给指定元素输入值
2. 需要让脚本模拟人为删除元素的内容
3. 需要让脚本模拟点击操作

4.2 方法

1. click() 单击元素
2. send_keys(value) 模拟输入
3. clear() 清除文本

4.3 案例

需求

需求:打开注册A页面,完成以下操作
1.通过脚本执行输入用户名:admin;密码:123456;电话号码:18611111111;电子邮件:123@qq.com
2.间隔3秒,修改电话号码为:18600000000
3.间隔3秒,点击‘注册’按钮
4.间隔3秒,关闭浏览器
ps: 元素定位方法不限

操作难点分析

1. 修改电话号码,先清除再输入新的号码; 清除 --> clear()
2. 点击按钮 --> click()

5 浏览器操作

5.1 应用场景

脚本启动浏览器窗口大小默认不是全屏?
如何刷新页面?

怎样解决这些问题?

5.2 方法

1. maximize_window()                 最大化浏览器窗口 --> 模拟浏览器最大化按钮
2. set_window_size(width, height)   设置浏览器窗口大小 --> 设置浏览器宽、高(像素点)
3. set_window_position(x, y)        设置浏览器窗口位置 --> 设置浏览器位置
4. back()                           后退 --> 模拟浏览器后退按钮
5. forward()                        前进 --> 模拟浏览器前进按钮
6. refresh()                        刷新 --> 模拟浏览器F5刷新
7. close()                          关闭当前窗口 --> 模拟点击浏览器关闭按钮
8. quit()                           关闭浏览器驱动对象 --> 关闭所有程序启动的窗口
9. title                            获取页面title
10. current_url                     获取当前页面URL

close关闭与quit关闭有什么区别?

close关闭的是当前页面,但是浏览器驱动还在,会造成内存泄露,最终导致内存溢出

quit关闭的是当前浏览器驱动

5.3 示例

# 最大化浏览器
driver.maximize_window()
# 刷新
driver.refresh()
# 后退
driver.back()
# 前进
driver.forward()
# 设置浏览器大小
driver.set_window_size(300,300)
# 设置浏览器位置
driver.set_window_position(300,200)
# 关闭浏览器单个窗口
driver.close()
# 关闭浏览器所有窗口
driver.quit()
# 获取title
title = driver.title
# 获取当前页面url
url = driver.current_url

6 获取元素信息

应用场景

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

is_displayed() 和is_enabled() 扩展

面试题:如果你定位不到一个元素,请问怎么去排查?

6.3 案例

需求:使用‘注册A.html’页面,完成以下操作:
1.获取用户名输入框的大小
2.获取页面上第一个超链接的文本内容
3.获取页面上第一个超链接的地址
4.判断页面中的span标签是否可见
5.判断页面中取消按钮是否可用
6.判断页面中'旅游'对应的复选框是否为选中的状态

代码:

# 1.获取用户名输入框的大小
print(driver.find_element_by_tag_name("a").size)
# 2.获取页面上第一个超链接的文本内容
print(driver.find_element_by_tag_name("a").text)
# 3.获取页面上第一个超链接的地址
print(driver.find_element_by_tag_name("a").get_attribute("href"))
# 4.判断页面中的span标签是否可见
print(driver.find_element_by_tag_name("span").is_displayed())
# 5.判断页面中取消按钮是否可用
print(driver.find_element_by_id("cancelA").is_enabled())
# 6.判断页面中'旅游'对应的复选框是否为选中的状态
print(driver.find_element_by_id("lyA").is_selected())

7 鼠标操作

7.1 什么是鼠标操作

点击、右击、双击、悬停、拖拽等

7.2 应用场景

现在Web产品中存在丰富的鼠标交互方式,作为一个Web自动化测试框架,需要应对这些鼠标操作的应用场景

7.3 常用方法

说明:在Selenium中将操作鼠标的方法封装在ActionChains类中实例化对象:
action = ActionChains(driver)方法:
1. context_click(element)           右击 --> 模拟鼠标右键点击效果
2. double_click(element)            双击 --> 模拟鼠标双击效果
3. drag_and_drop(source, target)    拖动 --> 模拟鼠标拖动效果
4. move_to_element(element)         悬停 --> 模拟鼠标悬停效果
5. perform()                        执行 --> 此方法用来执行以上所有鼠标操作为了更好的学习其他方法,我们先学习perform()执行方法,因为所有的方法都需要执行才能生效

7.4 鼠标执行-perform()

说明:在ActionChains类中所有提供的鼠标事件方法,在调用的时候所有的行为都存储在ActionChains对象中, 而perform()方法就是真正去执行所有的鼠标事件强调:必须调用perform()方法才能执行鼠标事件

7.5 鼠标右键-context_click()

说明:
对于点击鼠标右键,如果弹出的是浏览器默认的菜单,Selenium没有提供操作菜单选项的方法;
如果是自定义的右键菜单,则可以通过元素定位来操作菜单中的选项

练习

需求:打开注册页面A,在用户名文本框上点击鼠标右键

关键点分析

1. 导包:from selenium.webdriver.common.action_chains import ActionChains
2. 实例化ActionChains对象:action = ActionChains(driver)
3. 调用右键方法:action.context_click(element)
4. 执行:action.perform()

核心代码:

element = driver.find_element_by_id("userA")
action = ActionChains(driver).context_click(element)
action.perform()

7.6 鼠标双击-double_click()

说明:模拟双击鼠标左键操作

练习

需求:打开注册页面A,输入用户名admin,暂停3秒钟后,双击鼠标左键,选中admin

核心代码:

element = driver.find_element_by_id("userA")
element.send_keys("admin")
time.sleep(3)
ActionChains(driver).double_click(element).perform()

7.7 鼠标拖动-drag_and_drop()

说明:模拟鼠标拖动动作,选定拖动源元素释放到目标元素

关键点分析

1. 源元素        source = driver.find_element_by_id(xxx)
2. 目标元素     target = driver.find_element_by_id(xxx)
3. 调用方法     action.drag_and_drop(source, target).perform()

练习

需求:打开‘drag.html’页面,把红色方框拖拽到蓝色方框上

核心代码:

source = driver.find_element_by_id("div1")
target = driver.find_element_by_id("div2")
ActionChains(driver).drag_and_drop(source, target).perform()

7.8 鼠标悬停-move_to_element()

说明: 模拟鼠标悬停在指定的的元素上

练习

需求:打开注册页面A,模拟鼠标悬停在‘注册’按钮上

核心代码:

element = driver.find_element_by_xpath("/html/body/div/fieldset/form/p[5]/button")
ActionChains(driver).move_to_element(element).perform()

扩展:

鼠标操作ActionChains可进行链式调用,单击后可继续调用双击继续调用右键,可执行一连串操作

8 键盘操作

8.1 应用场景

思考:

如何实现复制/ 粘贴的操作?

说明:
1. 模拟键盘上一些按键或者组合键的输入 如:Ctrl+C 、Ctrl+V;
2. Selenium中把键盘的按键都封装在Keys类中

8.2 常用操作

导包:from selenium.webdriver.common.keys import Keys1. send_keys(Keys.BACK_SPACE)   删除键(BackSpace)
2. send_keys(Keys.SPACE)        空格键(Space)
3. send_keys(Keys.TAB)          制表键(Tab)
4. send_keys(Keys.ESCAPE)       回退键(Esc)
5. send_keys(Keys.ENTER)        回车键(Enter)
6. send_keys(Keys.CONTROL,'a')  全选(Ctrl+A)
7. send_keys(Keys.CONTROL,'c')  复制(Ctrl+C)提示:以上方法就不一个一个讲解了,因为调用方法都一样

8.3 案例

需求:打开注册A页面,完成以下操作
1. 输入用户名:admin1,暂停2秒,删除1
2. 全选用户名:admin,暂停2秒
3. 复制用户名:admin,暂停2秒
4. 粘贴到密码框

8.4 案例代码

# 定位用户名
element = driver.find_element_by_id("userA")
# 输入用户名
element.send_keys("admin1")
# 删除1
element.send_keys(Keys.BACK_SPACE)
# 全选
element.send_keys(Keys.CONTROL, 'a')
# 复制
element.send_keys(Keys.CONTROL, 'c')
# 粘贴
driver.find_element_by_id('passwordA').send_keys(Keys.CONTROL, 'v')

9 元素等待

9.1 概念

定位页面元素时如果未找到,在指定时间内一直等待的过程

元素等待一共分为两种类型

  1. 隐式等待
  2. 显式等待

9.2 应用场景

由于一些原因,我们想找的元素并没有立刻出来,此时如果直接定位会报错,场景如下:

  1. 网络速度慢
  2. 服务器处理请求速度慢
  3. 硬件配置原因

是否定位每个元素都应该需要元素等待?

9.3 隐式等待

方法

# 隐式等待为全局设置(只需要设置一次,就会作用于所有元素)
# 参数:
#   timeout:超时的时长,单位:秒
driver.implicitly_wait(timeout)

案例

需求:打开注册A页面,完成以下操作
使用隐式等待定位用户名输入框,如果元素存在,就输入admin

注意点

单个元素定位超时会报 NoSuchElementException异常

9.4 显式等待

在Selenium中把显式等待的相关方法封装在 WebDriverWait 类中

方法

# 显式等待,为定位不同元素的超时时间设置不同的值1. 导包 from selenium.webdriver.support.wait import WebDriverWait
2. WebDriverWait(driver, timeout, poll_frequency=0.5)1). driver:浏览器驱动对象2). timeout:超时的时长,单位:秒3). poll_frequency:检测间隔时间,默认为0.5秒
3. 调用方法 until(method):直到...时1). method:函数名称,该函数用来实现对元素的定位2). 一般使用匿名函数来实现:lambda x: x.find_element_by_id("userA")如:ele = WebDriverWait(driver, 10, 1).until(lambda x: x.find_element_by_id("userA"))

一般函数的定义:

def test():print("test")
#调用方式
test()

匿名函数:

test = lambda x , y : x+y
#调用
test(1,2)
#结果为3
print(test(1,2))

案例

需求:打开注册A页面,完成以下操作
使用显示等待定位用户名输入框,如果元素存在,就输入admin

注意点

单个元素定位超时会报 TimeoutException异常

9.5 隐式和显式区别

1. 作用域:隐式为全局元素,显式等待为单个元素有效
2. 使用方法:隐式等待直接通过驱动对象调用,而显式等待方法封装在WebDriverWait类中
3. 达到最大超时时长后抛出的异常不同:隐式为NoSuchElementException,显式等待为TimeoutException

10 下拉框/弹出框/滚动条操作

10.1 下拉框

10.1.1 方法一

案例

需求:使用‘注册A.html’页面,完成对城市的下拉框的操作
1.选择‘广州’
2.暂停2秒,选择‘上海’
3.暂停2秒,选择‘北京’

核心代码

driver.find_element_by_xpath("//*[@id='selectA']/option[3]").click()
time.sleep(2)
driver.find_element_by_xpath("//*[@id='selectA']/option[2]").click()
time.sleep(2)
driver.find_element_by_xpath("//*[@id='selectA']/option[1]").click()

说明: 下拉框就是 HTML 中的 元素

思路: 先定位到操作的 option 元素, 然后执行点击操作

问题: 操作起来比较繁琐

10.1.2 方法二

Select类

说明:Select类是Selenium为操作select标签特殊封装的实例化对象:select = Select(element)element: <select>标签对应的元素,通过元素定位方式获取例如:driver.find_element_by_id("selectA")操作方法:
1. select_by_index(index)       --> 根据option索引来定位,从0开始
2. select_by_value(value)       --> 根据option属性 value值来定位
3. select_by_visible_text(text) --> 根据option显示文本来定位

Select类实现步骤分析

1. 导包 Select类 --> from selenium.webdriver.support.select import Select
2. 实例化Select类 select = Select(driver.find_element_by_id("selectA"))
3. 调用方法:select.select_by_index(index)

案例核心代码

select = Select(driver.find_element_by_xpath("//*[@id='selectA']"))
select.select_by_index(2)
time.sleep(2)
select.select_by_index(1)
time.sleep(2)
select.select_by_index(0)

10.2 弹出框

10.2.1 分类
网页中常用的弹出框有三种
1. alert    警告框
2. confirm  确认框
3. prompt   提示框
10.2.2 案例
需求:打开注册A.html页面,完成以下操作:
1.点击 alert 按钮
2.输入用户名:admin

错误代码:

driver.find_element_by_xpath("//*[@id='alerta']").click()
driver.find_element_by_id("userA").send_keys("admin")

思考

按钮被点击后弹出警告框,而接下来输入用户名的语句没有生效
1. 什么问题导致的?
2. 如何处理警告框?
10.2.3 方法
说明:Selenium中对处理弹出框的操作,有专用的处理方法;并且处理的方法都一样
1. 获取弹出框对象alert = driver.switch_to.alert
2. 调用alert.text         --> 返回alert/confirm/prompt中的文字信息alert.accept()   --> 接受对话框选项alert.dismiss() --> 取消对话框选项
10.2.4 示例代码

核心代码

driver.find_element_by_xpath("//*[@id='alerta']").click()
time.sleep(2)
alert = driver.switch_to.alert
time.sleep(2)
print(alert.text)
time.sleep(2)
alert.accept()
time.sleep(2)
driver.find_element_by_id("userA").send_keys("admin")

10.3 滚动条

10.3.1 应用场景
1. 在HTML页面中,由于前端技术框架的原因,页面元素为动态显示,元素根据滚动条的下拉而被加载
2. 页面注册同意条款,需要滚动条到最底层,才能点击同意
10.3.2 实现方式

说明:

selenium中没有提供操作滚动条的方法,但是它提供了可执行JavaScript脚本的方法,所以我们可以通过JavaScript脚本来达到操作滚动条的目的

1. 设置JavaScript脚本控制滚动条js = "window.scrollTo(0,1000)"(0:左边距;1000:上边距;单位像素)
2. selenium调用执行JavaScript脚本的方法driver.execute_script(js)

JavaScript是一种被广泛用于Web前端开发的脚本语言 ,常用来为网页添加动态功能(弹窗,点击事件…),JavaScript提供了页面对象获取和操作功能。

实现方式一:
编写js脚本 定位用户名输入框,并且输入admin
js = "document.getElementById('userA').value='admin'"
#执行js脚本
driver.execute_script(js)#实现方式二:
#编写js定位元素并且返回定位到的元素
js = "return document.getElementById('userA')"
#将定位到的元素赋予el这个变量
el = driver.execute_script(js)
#输入admin
el.send_keys('admin')

如何去开发者工具调试js语句?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SzWlNqtB-1609329063538)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201230150516102.png)]

10.3.3 案例
需求:打开注册页面A,暂停2秒后,滚动条拉到最底层

示例代码

js1 = "window.scrollTo(0,10000)" # 最底部
js2 = "window.scrollTo(0,0)"        # 最顶部
time.sleep(2)
driver.execute_script(js1)
time.sleep(2)
driver.execute_script(js2)

11 frame切换

11.1 概念

frame:HTML页面中的一种框架,主要作用是在当前页面中指定区域显示另一页面元素;
形式一:[了解]<frameset cols="25%,75%"><frame src="frame_a.htm"><frame src="frame_b.htm"></frameset>
形式二:<iframe name="iframe_a" src="demo_iframe.htm" width="200" height="200"></iframe>

11.2 案例

需求:打开‘注册实例.html’页面,完成以下操作
1. 填写主页面的注册信息
2. 填写注册页面A中的注册信息
3. 填写注册页面B中的注册信息

问题:

当前页面内无法定位注册页面A和注册页面B

11.3 方法

说明:在Selenium中封装了如何切换frame框架的方法步骤:1. driver.switch_to.frame(frame_reference)    --> 切换到指定frame的方法frame_reference:可以为frame框架的name、id或者定位到的frame元素2. driver.switch_to.default_content()         --> 恢复默认页面方法在frame中操作其他页面,必须先回到默认页面,才能进一步操作

解决方案:

  1. 完成主页面注册信息;
  2. 调用frame切换方法(switch_to.frame(“myframe1”))切换到注册用户A框架中
  3. 调用恢复默认页面方法(switch_to.default_content())
  4. 调用frame切换方法(switch_to.frame(“myframe2”))切换到注册用户B框架中

11.4 示例代码

driver.find_element_by_id("userA").send_keys("admin")
# driver.switch_to.frame(driver.find_element_by_id("idframe1"))
# driver.switch_to.frame("idframe1")
driver.switch_to.frame("myframe1")
driver.find_element_by_id("userA").send_keys("adminA")
driver.switch_to.default_content()
driver.switch_to.frame("myframe2")
driver.find_element_by_id("userA").send_keys("adminB")

12 多窗口切换

12.1 概念

什么是窗口?

为什么要切换窗口?

在HTML页面中,当点击超链接或者按钮时,有的会在新的窗口打开页面

12.2 案例

需求:打开注册页面A
1. 新窗口打开新浪
2. 在新浪搜索框输入'新浪搜索'
3. 在注册页输入用户名admin

12.3 方法

说明:在Selenium中封装了获取当前窗口句柄、获取所有窗口句柄和切换到指定句柄窗口的方法
句柄:英文handle,窗口的唯一识别码
方法:1. driver.current_window_handle   --> 获取当前窗口句柄2. driver.window_handles             --> 获取所有窗口句柄3. driver.switch_to.window(handle)   --> 切换指定句柄窗口

解决方案:

  1. 打开注册A页面
  2. 获取当前窗口句柄
  3. 在注册A页面点击 “访问 新浪 网站”
  4. 获取所有窗口句柄
  5. 根据句柄, 切换到新浪窗口
  6. 对输入框进行输入
  7. 切换回原窗口, 即注册A窗口
  8. 输入用户名 admin

注意: 新浪页面加载慢, 可能需要用到元素等待

12.4 示例代码

driver.implicitly_wait(10)
print("切换前窗口句柄:", driver.current_window_handle)
driver.find_element_by_id("fw").click()
handles = driver.window_handles
print("所有窗口句柄:", handles)
driver.switch_to.window(handles[1])
print("切换后窗口句柄:", driver.current_window_handle)
driver.find_element_by_class_name("inp-txt").clear()
driver.find_element_by_class_name("inp-txt").send_keys("1111111111111")
time.sleep(3)
driver.switch_to.window(handles[0])
print("切换回原窗口句柄:", driver.current_window_handle)
driver.find_element_by_id("userA").send_keys("admin")

13 窗口截图

13.1 概念

什么是窗口截图?

把当前操作的页面,截图保存到指定位置

为什么要窗口截图?

自动化脚本是由程序去执行的,因此有时候打印的错误信息并不是十分明确

如果在执行出错的时候对当前窗口截图保存,那么通过图片就可以非常直观地看到出错的原因

13.2 方法

说明:在Selenium中,提供了截图方法,我们只需要调用即可方法:driver.get_screenshot_as_file(imgpath)imgpath:图片保存路径

13.3 案例

需求:打开‘注册A.html’页面,完成以下操作
1. 填写注册信息
2. 截图保存

核心代码:

driver.find_element_by_id("userA").send_keys("admin")
# 需提前创建 png 目录
driver.get_screenshot_as_file("./png/123.png")ps:filepath = "./img/test_{}.png".format(time.strftime("%Y%m%d%H%M%S"))

14 验证码处理

14.1 概念

什么是验证码?

一种随机生成的信息(数字、字母、汉字、图片、算术题)等为了防止恶意的请求行为,增加应用的安全性

为什么要学习验证码?

在Web应用中,大部分系统在用户登录注册的时候都要求输入验证码,而我们在设计自动化测试脚本的时候,就需要面临处理验证码的问题

14.2 常用方法

说明:Selenium中并没有对验证码处理的方法,在这里我们介绍一下针对验证码的几种常用处理方式方式:1. 去掉验证码(测试环境下-采用)2. 设置万能验证码(生产环境和测试环境下-采用)3. 验证码识别技术(通过Python-tesseract来识别图片类型验证码;识别率很难达到100%)4. 记录cookie(通过记录cookie进行跳过登录)

PS

1. 去掉验证码、设置万能验证码:都是开发来完成,我们在这里不做讲解
2. 验证码识别技术:成功率不高,验证码种类繁多,不太适合
3. 记录cookie:比较实用,我们对它进行下讲解

14.3 cookie

14.3.1 概念
1. Cookie是由Web服务器生成的,并且保存在用户浏览器上的小文本文件,它可以包含用户相关的信息
2. Cookie数据格式:键值对组成(python中的字典)
3. Cookie产生:客户端请求服务器,如果服务器需要记录该用户状态,就向客户端浏览器颁发一个Cookie数据
4. Cookie使用:当浏览器再次请求该网站时,浏览器把请求的数据和Cookie数据一同提交给服务器,服务器检查该  Cookie,以此来辨认用户状态

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-liiZ3JXN-1609329063540)(.\Web自动化测试.assets\image-20200104103932686.png)]

14.3.2 应用场景
1. 实现会话跟踪,记录用户登录状态
2. 实现记住密码和自动登录的功能
3. 用户未登录的状态下,记录购物车中的商品

14.4 selenium操作cookie

14.4.1 方法
说明:Selenium中对cookie操作提供相应的方法方法:1. get_cookie(name)         --> 获取指定cookiename:为cookie的名称2. get_cookies()            --> 获取本网站所有本地cookies3. add_cookie(cookie_dict)   --> 添加cookiecookie_dict:一个字典对象,必选的键包括:"name" and "value"
14.4.2 案例
需求:使用cookie实现跳过登录
1. 手动登录百度,获取cookie
2. 使用获取到的cookie,达到登录目的,然后就可以执行登录之后的操作
14.4.3 实现步骤分析
BDUSS是登录百度后的唯一身份凭证(*.baidu.com),拿到BDUSS就等于拿到帐号的控制权,通行贴吧、知道、百科、文库、空间、百度云等百度主要产品。1. 登录baidu,登录成功后抓取 (BDUSS)
2. 使用add_cookie()方法,添加 (BDUSS)键和值
3. 调用刷新方法 driver.refresh()
14.4.4 示例代码
from selenium import webdriver
import timedriver = webdriver.Chrome()driver.get("http://www.baidu.com")
driver.add_cookie({'name': 'BDUSS', 'value': '根据实际填写'})
time.sleep(3)
driver.refresh()time.sleep(3)
driver.quit()

第三章 Pytest框架

1 总体介绍

1.1 什么是框架

说明:
1. 框架英文单词framework
2. 为解决一类事情的功能集合

1.2 什么是Pytest框架

Pytest框架 是一个第三方单元测试框架,用它来做单元测试

UnitTest 是Python自带的一个单元测试框架

1.3 为什么使用Pytest

1. 能够组织多个用例去执行
2. 提供丰富的断言方法
3. 能够生成测试报告

2 基本使用

2.1 安装

安装

pip install pytest
# pip install pytest==3.10

推荐安装 3.10 的版本, 兼容性较好

校验

  1. 进入命令行
  2. 输入命令 pytest --version 会展示当前已安装的版本

2.2 运行方式

2.2.1 方式介绍
方式1. 命令行模式【建议】
方式2. 主函数模式
2.2.2 代码准备

test_login.py

class TestLogin:def test_a(self):   # test开头的测试函数print("----->test_a")def test_b(self):print("----->test-b")
2.2.3 命令行运行
命令行中执行:
pytest -s test_login.py

-s 表示支持控制台打印,如果不加,print 不会出现任何内容

2.2.4 主函数运行
在 test_login.py 文件中增加主函数:
# 需要先导入包: import pytest
if __name__ == '__main__':pytest.main(["-s", "test_login.py"])
2.2.5 运行结果
test_login.py ----->test_a
.----->test_b
F

. 表示成功

F 表示失败

2.3 断言

2.3.1 什么是断言
让程序代替人为判断测试程序执行结果是否符合预期结果的过程
2.3.2 为什么学习断言
自动化脚本在执行的时候一般都是无人值守状态,我们不知道执行结果是否符合预期结果,所以我们需要让程序代替人为检测程序执行的结果是否符合预期结果,这就需要使用断言
2.3.3 断言方法
assert xx        -->判断xx为真
assert not xx   -->判断xx不为真
assert a in b   -->判断b包含a
assert a == b   -->判断a等于b
assert a != b   -->判断a不等于b

对比UinitTest的断言:

image-20200104133541983

2.3.4 案例
需求:
1. 写一个计算加法的函数, 要求可以输入两个参数
2. 写两个测试脚本, 测试加法计算结果, 一个断言成功, 另一个断言失败
3. 运行并查看结果
2.3.4 示例代码

test_demo.py

def add(x, y):return x + yclass TestDemo:def test_a(self):assert 2 == add(1, 1)def test_b(self):assert 4 == add(2, 1)

3 setup和teardown

3.1 应用场景

pytest 在运行自动化脚本的前后会执行两个特殊的方法,分别是“前置” 和“后置”方法。
在执行脚本前会执行“前置”方法,在执行脚本后会执行 “后置”方法。
有了这两个方法,我们可以在“前置”方法中获取驱动对象,在“后置”方法中关闭驱动对象。

3.2 概念和方法

1. 初始化(前置处理):def setup(self)     --> 首先自动执行
2. 销毁(后置处理):def teardown(self)  --> 最后自动执行
3. 运行于测试方法的始末,即:运行一次测试方法就会运行一次setUp和tearDown

3.3 案例

需求:在一个测试类中定义多个测试方法,查看每个测试方法执行完所花费的时长

3.4 示例代码

test_demo.py

import timedef add(x, y):return x + yclass TestDemo:def setup(self):print("start-time:", time.time())def teardown(self):print("end-time:", time.time())def test_a(self):assert 2 == add(1, 1)def test_b(self):assert 3 == add(2, 1)

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_*.py
python_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 进行安装

install pytest-html==1.21.1 在最新的2.0上有错误

5.3 使用

在配置文件中的命令行参数中增加 --html=用户路径/report.html

示例: pytest.ini 中

addopts = -s --html=report/report.html

5.4 生成报告

步骤:
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)

结果

scripts\test_login.py zhangsan
.lisi
.

6.6 多个参数

示例

# 要求:
# 使用多个参数的数据参数化, 分别打印2组账号和密码: zhangsan / 111111 和 lisi / 222222
    @pytest.mark.parametrize(("username", "password"), [("zhangsan","111111"), ("lisi","222222")])def test_a(self, username, password):print(username + "---" + password)

结果

scripts\test_login.py zhangsan---111111
.lisi---222222
.

6.7 推荐用法

示例

# 要求:
# 使用扩展, 增加数据可读性, 使用字典作为参数列表中的每个元素, 修改上面得案例
    @pytest.mark.parametrize("params", [{"username": "zhangsan","password": "111"},{"username": "lisi","password": "222"}])def test_a(self, params):print(params)

结果

scripts\test_login.py {'username': 'zhangsan', 'password': '111'}
.{'username': 'lisi', 'password': '222'}
.

第四章 PO模式

学习路线

  • V1: 不使用任何设计模式和单元测试框 (面向过程式)
  • V2: 使用 Pytest 管理用例
  • V3: 使用方法封装的思想, 对代码进行优化
  • V4: 采用PO模式的分层思想对代码进行拆分, 分离page
  • V5: 对PO分层之后的代码继续优化, 分离page中的元素和操作
  • V6: PO模式深入封装, 把共同操作提取封装

1 无模式

1.1 案例说明

对TPshop项目的登录模块进行自动化测试

提示:登录模块包含了很多测试用例,比如:账号不存在、密码错误、验证码错误、登录成功等等
为了节省时间我们只选取几个有代表性的用例来演示

1.2 选择测试用例

  • 账号不存在

    1. 点击首页的 ‘登录’ 链接,进入登录页面
    2. 输入一个不存在的用户名
    3. 输入密码
    4. 输入验证码
    5. 点击登录按钮
    6. 获取错误提示信息
  • 密码错误
    1. 点击首页的 ‘登录’ 链接,进入登录页面
    2. 输入用户名
    3. 输入一个错误的密码
    4. 输入验证码
    5. 点击登录按钮
    6. 获取错误提示信息

1.3 v1版本

1.3.1 总体介绍
  • 不使用任何设计模式和单元测试框架
  • 每个文件里编写一个用例,完全的面向过程的编程方式
1.3.2 示例代码
  • 登录功能, 账号不存在, test_login_account_not_exist.py

    # 账号不存在
    import time
    from selenium import webdriver# 实例化浏览器驱动
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.implicitly_wait(10)
    driver.get('http://localhost/')# 1. 点击首页的‘登录’链接,进入登录页面
    driver.find_element_by_class_name('red').click()
    # 2. 输入一个不存在的用户名
    driver.find_element_by_id('username').send_keys('17100000000')
    # 3. 输入密码
    driver.find_element_by_id('password').send_keys('123456')
    # 4. 输入验证码
    driver.find_element_by_id('verify_code').send_keys('8888')
    # 5. 点击登录按钮
    driver.find_element_by_name('sbtbutton').click()
    # 6. 获取错误提示信息
    # msg = driver.find_element_by_css_selector('.layui-layer-content').text
    msg = driver.find_element_by_xpath("//*[@id='layui-layer1']/div[2]").text
    print(msg)# 关闭浏览器
    time.sleep(5)
    driver.quit()
    
  • 登录功能, 密码错误, test_login_password_error.py

    # 密码错误
    import time
    from selenium import webdriver# 实例化浏览器驱动
    driver = webdriver.Chrome()
    driver.maximize_window()
    driver.implicitly_wait(10)
    driver.get('http://localhost/')# 1. 点击首页的‘登录’链接,进入登录页面
    driver.find_element_by_class_name('red').click()
    # 2. 输入一个已存在的用户名
    driver.find_element_by_id('username').send_keys('17150312012')
    # 3. 输入错误密码
    driver.find_element_by_id('password').send_keys('error')
    # 4. 输入验证码
    driver.find_element_by_id('verify_code').send_keys('8888')
    # 5. 点击登录按钮
    driver.find_element_by_name('sbtbutton').click()
    # 6. 获取错误提示信息
    msg = driver.find_element_by_css_selector('.layui-layer-content').text
    print(msg)# 关闭浏览器
    time.sleep(5)
    driver.quit()
    
1.3.3 存在的问题
  • 一条测试用例对应一个文件,用例较多时不方便管理维护
  • 代码高度冗余

1.4 v2版本

1.4.1 总体介绍

引入Pytest管理用例, 并断言用例的执行结果

好处

  • 方便组织、管理多个测试用例
  • 提供了丰富的断言方法
  • 方便生成测试报告
  • 减少了代码冗余
1.4.2 示例代码
# 1.导包
import time
from selenium import webdriver# 2.定义测试类
class TestLogin:def setup(self):self.driver = webdriver.Chrome()self.driver.maximize_window()self.driver.implicitly_wait(10)self.driver.get('http://localhost/')def teardown(self):# 关闭浏览器time.sleep(5)self.driver.quit()# 定义账户不存在测试方法def test_login_accout_not_exist(self):# 1. 点击首页的‘登录’链接,进入登录页面self.driver.find_element_by_class_name('red').click()# 2. 输入一个不存在的用户名self.driver.find_element_by_id('username').send_keys('17100000000')# 3. 输入密码self.driver.find_element_by_id('password').send_keys('123456')# 4. 输入验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息msg = self.driver.find_element_by_css_selector('.layui-layer-content').textprint(msg)# 断言assert "账号不存在!" == msg# 定义密码错误的测试def test_login_password_error(self):# 1. 点击首页的‘登录’链接,进入登录页面self.driver.find_element_by_class_name('red').click()# 2. 输入一个已存在的用户名self.driver.find_element_by_id('username').send_keys('17150312012')# 3. 输入错误密码self.driver.find_element_by_id('password').send_keys('error')# 4. 输入验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息msg = self.driver.find_element_by_css_selector('.layui-layer-content').textprint(msg)# 断言assert "密码错误!" == msg
1.4.3 存在的问题

代码冗余

#获取驱动的代码冗余,如果有多个test文件,则需要多次重复编写创建驱动

2 方法封装

2.1 封装方法

概念

是将一些有共性的或多次被使用的代码提取到一个方法中,供其他地方调用

好处

  • 避免代码冗余
  • 容易维护
  • 隐藏代码实现的细节

目的

用最少的代码实现最多的功能

2.2 v3版本

2.2.1 总体介绍

使用方法封装的思想,对代码进行优化

2.2.2 封装驱动

定义获取驱动对象的工具类

utils.py

# 获取/关闭浏览器驱动的类
from selenium import webdriverclass DriverUtils:__driver = None# 获取浏览器驱动@classmethoddef get_driver(cls):if cls.__driver is None:cls.__driver = webdriver.Chrome()cls.__driver.maximize_window()cls.__driver.implicitly_wait(10)return cls.__driver# 关闭浏览器驱动@classmethoddef quit_driver(cls):if cls.__driver is not None:cls.__driver.quit()cls.__driver = None

test_login.py

# 1.导包
import time
from selenium import webdriver
from utils import DriverUtils# 2.定义测试类
class TestLogin:def setup(self):self.driver = DriverUtils.get_driver()self.driver.get('http://localhost/')def teardown(self):DriverUtils.quit_driver()# 定义账户不存在测试方法def test_login_accout_not_exist(self):# 1. 点击首页的‘登录’链接,进入登录页面self.driver.find_element_by_class_name('red').click()# 2. 输入一个不存在的用户名self.driver.find_element_by_id('username').send_keys('17100000000')# 3. 输入密码self.driver.find_element_by_id('password').send_keys('123456')# 4. 输入验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息msg = self.driver.find_element_by_css_selector('.layui-layer-content').textprint(msg)# 断言assert "账号不存在!" == msg# 定义密码错误的测试def test_login_password_error(self):# 1. 点击首页的‘登录’链接,进入登录页面self.driver.find_element_by_class_name('red').click()# 2. 输入一个已存在的用户名self.driver.find_element_by_id('username').send_keys('17150312012')# 3. 输入错误密码self.driver.find_element_by_id('password').send_keys('error')# 4. 输入验证码self.driver.find_element_by_id('verify_code').send_keys('8888')# 5. 点击登录按钮self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息msg = self.driver.find_element_by_css_selector('.layui-layer-content').textprint(msg)# 断言assert "密码错误!" == msg
2.2.3 存在的问题

代码冗余

3 PO模式介绍

3.1 思考

在做UI自动化时定位元素特别依赖页面,一旦页面发生变更就不得不跟着去修改定位元素的代码

举例:

假设要对一个元素进行点击操作,而且会经常用到,那么你就可能会编写多处如下代码

driver.find_element_by_id("login-btn").click()

存在的问题

  • 如果开发人员修改了这个元素的id,这时候你就不得不修改所有对应的代码
  • 存在大量冗余代码

如何解决呢?

3.2 PO模式概念(一个页面一个对象)

PO是Page Object的缩写,PO模式是自动化测试项目开发实践的最佳设计模式之一

核心思想:

  • 通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化, 只需要调整页面元素封装的代码,提高测试用例的可维护性、可读性
  • 页面和测试脚本分离

3.3 PO模式分层

分层机制,让不同层去做不同类型的事情,让代码结构清晰,增加复用性

主要有以下几种分层方式

  1. 两层: 对象操作层+业务数据层

    • 对象操作层: 封装页面信息, 包括元素以及对元素的操作

      • 对象操作层中,页面的元素是对象,点击输入等行为是操作
    • 业务数据层: 封装多操作组合的业务以及测试数据
      • 业务数据层中,test脚本中执行的是业务流程只需要关注业务,并且测试数据也是在此层传入
  2. 三层:对象库层+操作层+业务数据层

    • 对象层:封装页面元素的定位方法
    • 操作层:封装对元素的操作 (点击输入等)
    • 业务数据层:将一个或多个操作组合起来完成一个业务功能。比如登录:需要输入帐号、密码、点击登录三个操作,以及操作所需要的数据
  3. 四层:对象库层+操作层+业务层+数据层

3.4 PO模式的优点

  • 引入PO模式前

    • 存在大量冗余代码
    • 业务流程不清晰
    • 后期维护成本大
  • 引入PO模式后
    • 减少冗余代码
    • 业务代码和测试代码被分开,降低耦合性
    • 维护成本低

4 PO模式实践

4.1 v4版本

4.1.1 总体介绍

采用PO模式的分层思想对代码进行拆分

4.2.2 PO封装
  • 对登录页面进行封装

    LoginPage

  • 编写测试用例

    TestLogin

4.2.3 示例代码

结构

- utils-- utils.py
- page-- login_page.py
- scripts-- test_login.py
- pytest.ini

核心代码

utils.py

class DriverUtils:__driver = None# 获取浏览器驱动@classmethoddef get_driver(cls):if cls.__driver is None:cls.__driver = webdriver.Chrome()cls.__driver.maximize_window()cls.__driver.implicitly_wait(10)return cls.__driver# 关闭浏览器驱动@classmethoddef quit_driver(cls):if cls.__driver is not None:cls.__driver.quit()cls.__driver = None

login_page.py

class LoginPage:def __init__(self, driver):self.driver = driver# 1. 点击首页的‘登录’链接,进入登录页面def click_login_link(self):return self.driver.find_element_by_class_name('red').click()# 2. 输入一个不存在的用户名def input_username(self, username):return self.driver.find_element_by_id('username').send_keys(username)# 3. 输入密码def input_password(self, password):return self.driver.find_element_by_id('password').send_keys(password)# 4. 输入验证码def input_verify_code(self, code):return self.driver.find_element_by_id('verify_code').send_keys(code)# 5. 点击登录按钮def click_login_btn(self):return self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息def get_msg(self):msg = self.driver.find_element_by_css_selector('.layui-layer-content').textreturn msg

test_login.py

import time
from page.login_page import LoginPage
from utils.utils import DriverUtilsclass TestLogin:def setup(self):self.driver = DriverUtils.get_driver()self.login_page = LoginPage(self.driver)self.driver.get('http://localhost/')def teardown(self):time.sleep(3)DriverUtils.quit_driver()# 定义账户不存在测试方法def test_login_accout_not_exist(self):# 1. 点击首页的‘登录’链接,进入登录页面self.login_page.click_login_link()# 2. 输入一个不存在的用户名self.login_page.input_username("17100000000")# 3. 输入密码self.login_page.input_password("123456")# 4. 输入验证码self.login_page.input_verify_code("8888")# 5. 点击登录按钮self.login_page.click_login_btn()# 6. 获取错误提示信息print(self.login_page.get_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("17150312012")# 3. 输入错误密码self.login_page.input_password("error")# 4. 输入验证码self.login_page.input_verify_code("8888")# 5. 点击登录按钮self.login_page.click_login_btn()# 6. 获取错误提示信息print(self.login_page.get_msg())# 断言assert "密码错误!" == self.login_page.get_msg()

pytest.ini

[pytest]
addopts = -s
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_functions = test_*

可以看到test脚本下方法的操作都是一致的 只是用例的数据不一样,这时候我们可以使用参数化的形式:

@pytest.mark.parametrize("params",[{},{}])

4.2 v5版本

4.2.1 总体介绍

对PO分层后的代码继续优化

4.2.2 优化内容
  • 分离page页面中的元素和操作
  • 优化元素定位方式
4.2.3 示例代码

只变更了 login_page.py

from selenium.webdriver.common.by import Byclass 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 = driverdef find_el(self, feature):return self.driver.find_element(*feature)# 1. 点击首页的‘登录’链接,进入登录页面def click_login_link(self):return self.find_el(self.login_link_btn).click()# return self.driver.find_element(self.login_link_btn[0], self.login_link_btn[1]).click()# return self.driver.find_element_by_class_name('red').click()# 2. 输入一个不存在的用户名def input_username(self, username):return self.find_el(self.username_input).send_keys(username)# return self.driver.find_element_by_id('username').send_keys(username)# 3. 输入密码def input_password(self, password):return self.find_el(self.password_input).send_keys(password)# return self.driver.find_element_by_id('password').send_keys(password)# 4. 输入验证码def input_verify_code(self, code):return self.find_el(self.verify_code_input).send_keys(code)# return self.driver.find_element_by_id('verify_code').send_keys(code)# 5. 点击登录按钮def click_login_btn(self):return self.find_el(self.login_btn).click()# return self.driver.find_element_by_name('sbtbutton').click()# 6. 获取错误提示信息def get_msg(self):msg = self.find_el(self.msg_info).text# msg = self.driver.find_element_by_css_selector('.layui-layer-content').textreturn 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_aciton.py

class BaseAction:def __init__(self, driver):self.driver = driverdef find_el(self, feature):return self.driver.find_element(*feature)def find_els(self, feature):return self.driver.find_elements(*feature)def input(self, feature, context):return self.find_el(feature).send_keys(context)def click(self, feature):return self.find_el(feature).click()def clear(self, feature):return self.find_el(feature).clear

login_page.py

from selenium.webdriver.common.by import By
from base.base_action import BaseActionclass 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"# 1. 点击首页的‘登录’链接,进入登录页面def click_login_link(self):return self.click(self.login_link_btn)# return self.find_el(self.login_link_btn).click()# 2. 输入一个不存在的用户名def input_username(self, context):return self.input(self.username_input, context)# return self.find_el(self.username_input).send_keys(username)# 3. 输入密码def input_password(self, context):return self.input(self.password_input, context)# return self.find_el(self.password_input).send_keys(password)# 4. 输入验证码def input_verify_code(self, context):return self.input(self.verify_code_input, context)# return self.find_el(self.verify_code_input).send_keys(code)# 5. 点击登录按钮def click_login_btn(self):return self.click(self.login_btn)# return self.find_el(self.login_btn).click()# 6. 获取错误提示信息def get_msg(self):msg = self.find_el(self.msg_info).textreturn 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具有清晰的层级结构
  • 有效地提升网络传输效率

对比XML

XML 指可扩展标记语言, XML 被设计用来传输和存储数据

<?xml version="1.0" encoding="UTF-8" ?><name>tom</name><age>18</age><isMan>true</isMan><school /><address><country>中国</country><city>广州</city></address><numbers>2</numbers><numbers>4</numbers><numbers>6</numbers><numbers>8</numbers><like_links><name>Baidu</name><url>http://www.baidu.com</url></like_links><like_links><name>TaoBao</name><url>http://www.taobao.com</url></like_links>
{"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"}]
}

如果使用XML,需要读取XML文档,然后用XML DOM来遍历文档并读取值并存储在变量中
使用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
2.2.2 字典与JSON转换

把python字典类型转换为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# 读json文件"data.json"
with open("data.json", "r", encoding="utf-8") as f:data = json.load(f)print(data)# 把data2这个字典写入json文件"data2.json"
data2 = data
with open("data2.json", "w", encoding="utf-8") as f:json.dump(data2, f)

3 数据驱动实战一

为什么我们要把json文件的数据转换成字典呢?

如果需要读取数据,我们采用的是字典方式读取,而json数据是字符串,使用之前我们需要先转换成字典。

3.1 案例介绍

对tpshop项目的登录模块进行自动化测试, 需要使用数据驱动

为v6版本增加数据驱动

3.2 实现步骤

  1. 编写测试用例
  2. 采用PO模式的分层思想对页面进行封装
  3. 编写测试脚本
  4. 定义数据文件, 实现参数化

3.3 用例设计

账号不存在!

密码错误!

3.4 测试脚本参数化

核心代码

# 分析测试脚本参数化模板
dict1 = {"username": "17100000000", "password": "123456", "code": "8888", "msg": "账号不存在!"}dict2 = {"username": "17150312012", "password": "error", "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()print(self.login_page.get_msg())assert params["msg"] == self.login_page.get_msg()

3.5 数据文件

新建 ./data/login_data.json 文件

{"login_001_account_exist": {"username": "17100000000","password": "123456","code": "8888","msg": "账号不存在!"},"login_002_password_error": {"username": "17150312012","password": "error","code": "8888","msg": "密码错误!"}
}

3.6 数据解析函数

需要对json数据文件进行解析, 在test_login.py中, 写一个解析函数

def analyze_data():with open("./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 jsondef analyze_data(filename):with open("./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中进行调用, 核心代码如下

    @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()print(self.login_page.get_msg())assert params["msg"] == self.login_page.get_msg()

4 数据驱动实战二

4.1 案例介绍

对网页计算器,进行加法的测试操作。通过读取数据文件中的数据来执行用例

网址: http://cal.apple886.com/

4.2 实现步骤

  1. 编写测试用例
  2. 采用PO模式的分层思想对页面进行封装
  3. 编写测试脚本
  4. 定义数据文件, 实现参数化

4.3 copy框架代码

4.4 修改page文件

4.5 抽离数字定位方法

4.6 编写测试脚本

4.7 案例代码

目录

-- base-- base_action.py-- base_analyze.py
-- data-- cal_data.json
-- page-- cal_page.py
-- scripts-- test_add.py
-- utils-- utils.py
pytest.ini

代码

base和utils包的内容基本不变, 其中base_action.py 有新增数字定位方法

    def find_ele_num(self, feature, digit):return self.driver.find_element(feature[0], feature[1].format(str(digit)))

data/cal_data.json

{"add_001_xxx": {"data": [1, 2],"result": 3},"add_001_xxxx": {"data": [1, 2, 3],"result": 6}
}

page/cal_page.py

from selenium.webdriver.common.by import By
from base.base_action import BaseActionclass CalPage(BaseAction):# 数字 按钮digit_btn = By.ID, "simple{}"# 加法 按钮add_btn = By.ID, "simpleAdd"# 等号 按钮eq_btn = By.ID, "simpleEqual"# 计算结果result = By.ID, "resultIpt"# 点击数字def click_digit_btn(self, num):return self.find_ele_num(self.digit_btn, num).click()# 点击加法def click_add_btn(self):return self.click(self.add_btn)# 点击等号def click_eq_btn(self):return self.click(self.eq_btn)# 获取结果def get_result(self):return self.find_el(self.result).get_attribute("value")

scripts/test_add.py

import time
import pytest
from base.base_analyze import analyze_data
from page.cal_page import CalPage
from utils.utils import DriverUtilsclass TestCal:def setup(self):self.driver = DriverUtils.get_driver()self.cal_page = CalPage(self.driver)self.driver.get('http://cal.apple886.com/')def teardown(self):time.sleep(3)DriverUtils.quit_driver()@pytest.mark.parametrize("params", analyze_data("cal_data.json"))def test_add(self, params):for i in params["data"]:self.cal_page.click_digit_btn(i)self.cal_page.click_add_btn()self.cal_page.click_eq_btn()# print("---",self.cal_page.get_result())# print("+++",params["result"])assert self.cal_page.get_result() == str(params["result"])

第六章 日志收集

1 日志相关概念

1.1 日志的概念

日志就是用于记录系统运行时的信息, 对一个事件的记录, 也称为Log

1.2 日志的作用

  • 调试程序
  • 了解系统程序运行的情况,是否正常
  • 系统程序运行故障分析与问题定位
  • 用来做用户行为分析和数据统计

1.3 日志级别

思考:

是否系统记录的所有日志信息重要性都一样?

日志级别: 指日志信息的重要性

常见日志级别

日志级别 描述
DEBUG 调试级别,打印非常详细的日志信息,通常用于对代码的调试
INFO 信息级别,打印一般的日志信息,突出强调程序的运行过程
WARNING 警告级别,打印警告日志信息,表明会出现潜在错误的情形,一般不影响软件的正常使用
ERROR 错误级别,打印错误异常信息,该级别的错误可能会导致系统的一些功能无法正常使用
CRITICAL 严重错误级别,一个严重的错误,这表明系统可能无法继续运行

说明:

  • 上面列表中的日志级别是从上到下依次升高的,即:DEBUG < INFO < WARNING < ERROR < CRITICA
  • 当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息,而不是仅仅记录指定级别的日志信息
  • 一般建议只使用DEBUG、INFO、WARNING、ERROR这四个级别

2 日志基本用法

2.1 logging

Python中有一个标准库模块logging可以直接记录日志

2.2 基本用法

import logginglogging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告信息")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")

说明:

logging中默认的日志级别为WARNING,程序中大于等于该级别的日志才能输出

2.3 设置日志级别

方法

logging.basicConfig(level=logging.DEBUG)

如何选择日志级别?

  • 在开发环境和测试环境中,为了尽可能详细的查看程序的运行状态来保证上线后的稳定性,可以使用DEBUG或INFO级别的日志获取详细的日志信息,这是非常耗费机器性能的

  • 在生产环境中,通常只记录程序的异常信息、错误信息等(设置成WARNING或ERROR级别),这样既可以减小服务器的I/O压力,也可以提高获取错误日志信息的效率和方便问题的排查

2.4 设置日志格式

默认格式

日志级别:Logger名称:日志内容

自定义格式

logging.basicConfig(format="%(levelname)s:%(name)s:%(message)s")

format参数中可能用到的格式化信息:

占位符 描述
%(name)s Logger的名字
%(levelno)s 数字形式的日志级别
%(levelname)s 文本形式的日志级别
%(pathname)s 调用日志输出函数的模块的完整路径名,可能没有
%(filename)s 调用日志输出函数的模块的文件名
%(module)s 调用日志输出函数的模块名
%(funcName)s 调用日志输出函数的函数名
%(lineno)d 调用日志输出函数的语句所在的代码行
%(created)f 当前时间,用UNIX标准的表示时间的浮点数表示
%(relativeCreated)d 输出日志信息时的,自Logger创建以来的毫秒数
%(asctime)s 字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”
%(thread)d 线程ID。可能没有
%(threadName)s 线程名。可能没有
%(process)d 进程ID。可能没有
%(message)s 用户输出的消息

示例代码

import loggingfmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
logging.basicConfig(level=logging.DEBUG, format=fmt)logging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告信息")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")

2.5 日志输出到文件

默认情况下Python的logging模块将日志打印到了标准输出中(控制台)

将日志信息输出到文件的方法

logging.basicConfig(filename="a.log")

示例代码

import loggingfmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
logging.basicConfig(level=logging.DEBUG, format=fmt, filename="a.log")logging.debug("这是一条调试信息")
logging.info("这是一条普通信息")
logging.warning("这是一条警告信息")
logging.error("这是一条错误信息")
logging.critical("这是一条严重错误信息")

3 日志高级用法

3.1 总体介绍

思考:

  1. 如何将日志信息同时输出到控制台和日志文件中?
  2. 如何将不同级别的日志输出到不同的日志文件中?
  3. 如何解决日志文件过大的问题?

3.2 四大组件

3.2.1 总体介绍

logging日志模块四大组件

组件名称 类名 功能描述
日志器 Logger 提供了程序使用日志的入口
处理器 Handler 将logger创建的日志记录发送到合适的目的输出
格式器 Formatter 决定日志记录的最终输出格式
过滤器 Filter 提供了更细粒度的控制工具来决定输出哪条日志记录,丢弃哪条日志记录

logging模块就是通过这些组件来完成日志处理的

3.2.2 组件之间的关系
  • 日志器(logger)需要通过处理器(handler)将日志信息输出到目标位置,如:文件、sys.stdout、网络等
  • 不同的处理器(handler)可以将日志输出到不同的位置
  • 日志器(logger)可以设置多个处理器(handler)将同一条日志记录输出到不同的位置
  • 每个处理器(handler)都可以设置自己的格式器(formatter)实现同一条日志以不同的格式输出到不同的地方
  • 每个处理器(handler)都可以设置自己的过滤器(filter)实现日志过滤,从而只保留感兴趣的日志

简单点说就是:

日志器(logger)是入口,真正干活儿的是处理器(handler),处理器(handler)还可以通过过滤器(filter)和格式器(formatter)对要输出的日志内容做过滤和格式化等处理操作

3.2.3 Logger类

作用

  • 向程序暴露记录日志的方法
  • 将日志消息传送给所有感兴趣的日志handlers
  • 基于日志级别或Filter对象来决定要对哪些日志进行后续处理

如何创建Logger对象

logger = logging.getLogger()
logger = logging.getLogger("myLogger")
  • logging.getLogger()方法有一个可选参数name,该参数表示将要返回的日志器的名称标识

  • 如果不提供该参数,则返回root日志器对象

  • 若以相同的name参数值多次调用getLogger()方法,将会返回指向同一个logger对象的引用

Logger常用方法

方法 描述
logger.debug()
logger.info()
logger.warning()
logger.error()
logger.critical()
打印日志
logger.setLevel() 设置日志器将会处理的日志消息的最低严重级别
logger.addHandler() 为该logger对象添加一个handler对象
logger.addFilter() 为该logger对象添加一个filter对象
3.2.4 Handler类

作用

Handler对象的作用是将消息分发到handler指定的位置,比如:控制台、文件、网络、邮件等。 Logger对象可以通过addHandler()方法为自己添加多个handler对象

如何创建Handler对象

在程序中不应该直接实例化和使用Handler实例,因为Handler是一个基类,它只定义了Handler应该有的接口。 应该使用Handler实现类来创建对象,logging中内置的常用的Handler包括:

Handler 描述
logging.StreamHandler 将日志消息发送到输出到Stream,如std.out, std.err或任何file-like对象
logging.FileHandler 将日志消息发送到磁盘文件,默认情况下文件大小会无限增 长
logging.handlers.RotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按大小切割
logging.hanlders.TimedRotatingFileHandler 将日志消息发送到磁盘文件,并支持日志文件按时间切割
logging.handlers.HTTPHandler 将日志消息以GET或POST的方式发送给一个HTTP服务器
logging.handlers.SMTPHandler 将日志消息发送给一个指定的email地址

Handler常用方法

方法 描述
handler.setLevel() 设置handler将会处理的日志消息的最低严重级别
handler.setFormatter() 为handler设置一个格式器对象
handler.addFilter() 为handler添加一个过滤器对象
3.2.5 Formatter类

作用

Formatter对象用于配置日志信息的格式

如何创建Formatter对象

formatter = logging.Formatter(fmt=None, datefmt=None, style='%') fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"style:Python 3.2新增的参数,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'

3.3 案例

说明: 可读性好的日志需具备一些共性

  • 在控制台和文件都能输出
  • 文件输出能够按时间切割
# 1.导包
# 2.创建日志器对象 / 设置日志级别
# 3.创建输出到控制台 / 文件(按时间切割)的处理器对象
# 4.创建格式化器
# 5.将格式化器添加到处理器
# 6.添加处理器到日志器
# 7.打印日志

3.4 示例代码

# 1.导包
import logging.handlers
# 2.创建日志器对象 / 设置日志级别
logger = logging.getLogger("myLogger")
logger.setLevel(level=logging.DEBUG)
# 3.创建输出到控制台 / 文件(按时间切割)的处理器对象
ls = logging.StreamHandler()
lf = logging.handlers.TimedRotatingFileHandler(filename="test.log", when="s", backupCount=5)
# 4.创建格式化器
fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
formatter = logging.Formatter(fmt=fmt)
# 5.将格式化器添加到处理器
ls.setFormatter(formatter)
lf.setFormatter(formatter)
# 6.添加处理器到日志器
logger.addHandler(ls)
logger.addHandler(lf)
# 7.打印日志#试验
while 1:    def test_log():try:assert 1==2except Exception as e:logger.debug(e)test_log()

第七章 项目实战

1 自动化测试流程

1.1 流程

  1. 需求分析
  2. 挑选适合做自动化测试的功能
  3. 设计测试用例
  4. 搭建自动化测试环境 [可选]
  5. 设计自动化测试项目的架构 [可选]
  6. 编写代码
  7. 执行测试用例
  8. 生成测试报告并分析结果

1.2 对比

2 项目介绍

2.1 如何介绍[复习]

四个步骤

  1. 业务特性
  2. 用户和角色
  3. 功能模块
  4. 技术栈

2.2 项目案例介绍

项目名称

TPshop开源商城系统

项目描述

TPshop是一个电子商务B2C电商平台系统,功能强大,安全便捷。适合企业及个人快速构建个性化网上商城

包含PC+IOS客户端+Adroid客户端+微商城,系统PC+后台是基于ThinkPHP MVC构架开发的跨平台开源软件,设计得非常灵活,具有模块化架构体系和丰富的功能,易于与第三方应用系统无缝集成,在设计上,包含相当全面,以模块化架构体系,让应用组合变得相当灵活,功能也相当丰富

项目架构

windows + php + apache + mysql

要求能运用画图表达

3 用例设计

3.1 编写原则

  1. 自动化测试用例一般只实现核心业务流程或者重复执行率较高的功能
  2. 自动化测试用例的选择一般以“正向”逻辑的验证为主
  3. 不是所有手工用例都可以使用自动化测试来执行
  4. 尽量减少多个用例脚本之间的依赖
  5. 自动化测试用例执行完毕之后,一般需要回归原点

3.2 编写测试用例

4 搭建框架

4.1 新建项目

项目名称: webAutoTestTPshop

4.2 创建目录结构

  • base 包: 存放基类, 如 base_action.py, base_analyze.py
  • config 包: 存放配置文件, 如 logging_config.py
  • data 包: 存放测试数据, 如 login_data.json
  • log 包/文件夹: 存放日志文件
  • page 包: 存放页面类
  • report 包/文件夹: 存放html测试报告
  • screenshot 包/文件夹: 存放截图
  • scripts 包: 存放测试脚本
  • utils 包: 存放工具类, 如 utils.py
  • pytest.ini 文件

4.3 安装依赖包

  • selenium
  • pytest
  • pytest-html

4.4 初始化代码

  • 封装驱动工具类

    utils包下的 utils.py

  • 封装基类

    base包下的 base_action.py 和 base_analyze.py

5 编写代码

5.1 抽取PO

5.1.1 定义页面对象文件
登录页: login_page.py
首页: index_page.py
个人中心页: home_page.py
商品搜索页: goods_search_page.py
商品详情页: goods_detail_page.py
购物车页: cart_page.py
下订单页: order_page.py
订单支付页: order_pay_page.py
5.1.2 编写页面对象代码

5.2 编写测试脚本

5.2.1 定义脚本文件
登录模块: test_login.py
购物车模块: test_cart.py
订单模块: test_order.py
5.2.2 使用pytest管理脚本

6 完善代码

6.1 数据驱动

6.1.1 定义数据文件
  1. 定义存放测试文件的目录, 如 data

  2. 分模块定义数据文件

    login_data.py
    cart_data.py
    order_data.py
    
  3. 根据业务编写用例数据

6.1.2 测试数据参数化

使用 @pytest.mark.parametrize() 完成参数化

6.2 日志收集

6.2.1 日志配置

使用 logging 模块完成日志收集

示例代码

# 1.导包
import logging
import logging.handlers
import os# BASE_PATH = os.path.dirname(os.path.abspath("__file__"))
BASE_PATH = os.path.abspath(os.path.join(os.getcwd(), ".."))def set_log_config():# 2.创建日志器对象 / 设置日志级别logger = logging.getLogger()logger.setLevel(level=logging.INFO)# 3.创建输出到控制台 / 文件(按时间切割)的处理器对象ls = logging.StreamHandler()lf = logging.handlers.TimedRotatingFileHandler(filename="./log/tpshop.log", when="d", backupCount=5)# 4.创建格式化器fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'formatter = logging.Formatter(fmt=fmt)# 5.将格式化器添加到处理器ls.setFormatter(formatter)lf.setFormatter(formatter)# 6.添加处理器到日志器logger.addHandler(ls)logger.addHandler(lf)
6.2.2 配置文件加载

在 utils 包下

# 在 __init__.py 中增加
from config.logging_config import set_log_configset_log_config()
    |

| handler.addFilter() | 为handler添加一个过滤器对象 |

3.2.5 Formatter类

作用

Formatter对象用于配置日志信息的格式

如何创建Formatter对象

formatter = logging.Formatter(fmt=None, datefmt=None, style='%') fmt:指定消息格式化字符串,如果不指定该参数则默认使用message的原始值datefmt:指定日期格式字符串,如果不指定该参数则默认使用"%Y-%m-%d %H:%M:%S"style:Python 3.2新增的参数,可取值为 '%', '{'和 '$',如果不指定该参数则默认使用'%'

3.3 案例

说明: 可读性好的日志需具备一些共性

  • 在控制台和文件都能输出
  • 文件输出能够按时间切割
# 1.导包
# 2.创建日志器对象 / 设置日志级别
# 3.创建输出到控制台 / 文件(按时间切割)的处理器对象
# 4.创建格式化器
# 5.将格式化器添加到处理器
# 6.添加处理器到日志器
# 7.打印日志

3.4 示例代码

# 1.导包
import logging.handlers
# 2.创建日志器对象 / 设置日志级别
logger = logging.getLogger("myLogger")
logger.setLevel(level=logging.DEBUG)
# 3.创建输出到控制台 / 文件(按时间切割)的处理器对象
ls = logging.StreamHandler()
lf = logging.handlers.TimedRotatingFileHandler(filename="test.log", when="s", backupCount=5)
# 4.创建格式化器
fmt = '%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s'
formatter = logging.Formatter(fmt=fmt)
# 5.将格式化器添加到处理器
ls.setFormatter(formatter)
lf.setFormatter(formatter)
# 6.添加处理器到日志器
logger.addHandler(ls)
logger.addHandler(lf)
# 7.打印日志#试验
while 1:    def test_log():try:assert 1==2except Exception as e:logger.debug(e)test_log()

selenium实现web自动化测试相关推荐

  1. Web 自动化解决方案 [开源项目] 基于 Selenium 的 Web 自动化测试框架完美版

    欢迎查阅Selenium(Web自动化测试框架体系) Selenium ) Selenium是一个用于Web应用程序的自动化测试工具,直接运行在浏览器中,就像真正的用户在操作一样• 支持的浏览器包括I ...

  2. 论文python+selenium网页自动化测试论文_毕业设计(论文)-基于selenium的web自动化测试.docx...

    基于Selenium的Web自动化测试1 绪论1.1引言网络时代的到来和迅速普及,为软件产业带来了一场革命性的变化,基于Web的应用系统已经开始逐步取代原来的单机版应用系统,成为当前和未来的软件系统开 ...

  3. python+selenium进行web自动化测试

    python+selenium进行web自动化测试 一.配置python环境 目前已经更新到python3.10.3,我现在是3.9, 不过版本应该问题不大,大家可以自行官网下载并安装,并装好pych ...

  4. 软测入门(三)Selenium(Web自动化测试基础)

    Selenium(Web端自动测试) Selenium是一个用于Web应用程序测试的工具:中文是硒 开源 跨平台:linux.windows.mac 核心:可以在多个浏览器上进行自动化测试 多语言 S ...

  5. Selenium Web自动化测试——基于unittest框架的PO设计模式

    引言 前面一直在讲接口自动化测试框架与案例分享,很少讲Selenium这个Web自动化测试神器.它主要用来做UI自动化测试,大家都知道UI自动化测试成本相当高,一般的Web自动化测试我是一直不建议做的 ...

  6. Java+selenium简单实现web自动化测试

    最近在转行软件测试,有点艰难,为了提高自己的竞争力,只有不断的学习,学习多一点技术和知识. 这次要学习的是Java+Selenium的Web自动化测试,虽然这个自动化测试技术已经少有人用,或者说已经没 ...

  7. selenium - web 自动化测试

    1.什么是自动化 有效的减少人力的消耗, 同时提高生活的质量. 通过自动化测试有效减少人力的投入, 同时提高了测试的质量和效率. 1.1 为什么需要自动化测试 测试人员有个环节叫做回归测试. 回归测试 ...

  8. Sahi ---实现 Web 自动化测试

    参考网址:http://sahipro.com/docs/sahi-apis/index.html Sahi 是 Tyto Software 旗下的一个基于业务的开源 Web 应用自动化测试工具.Sa ...

  9. pythonselenium教程模拟鼠标和键盘_【02篇】python+selenium实现Web自动化:鼠标操作和键盘操作!...

    一.前言 最近问我自动化的人确实有点多,个人突发奇想:想从0开始讲解python+selenium实现Web自动化测试,请关注博客持续更新! 这是python+selenium实现Web自动化第二篇博 ...

最新文章

  1. 解决list-style-type属性失效
  2. finereport 登录界面的代码文件_Confluence 6 自定义登录界面
  3. 从闲扯开始我的技术博客吧
  4. 鸟哥Linux私房菜知识点总结3到5章
  5. 「干货总结」程序员必知必会的十大排序算法
  6. Linux——Vi/Vim如何消除搜索后的关键字高亮
  7. 微型计算机中外储存器比内储存器,计算机笔试复习题集共23页.doc
  8. Android 系统(78)---《android framework常用api源码分析》之 app应用安装流程
  9. 小白python自学—报错问题汇总(持续更新)
  10. docker的bridge网络
  11. 神泣服务器维护公告,《神泣》官方网站—创天互娱
  12. 《麦肯锡方法》读书笔记4
  13. 分享3个超棒的免费wordpress主题: Splus, EMagazine和BAGONG
  14. iVMS-8700综合安防管理平台第三方开发
  15. python之excel多表合并
  16. 螃蟹在剥我的壳,笔记本在写我。 漫天的我落在枫叶上雪花上。 而你在想我。
  17. 飞鱼手机短信语音验证码接收平台API接口例子
  18. Rviz 实现 pannel 插件
  19. CSDN博客个人主页左侧栏添加微信二维码
  20. Nebula Exchange 从Hive导入 NoSuchMethodError

热门文章

  1. Origin平台 建立EA账号时 显示“很抱歉,我们目前发生技术问题,请稍后再试一次”
  2. 纯手写原生PHP网站管理后台系统 网站管理系统
  3. Ubuntu16.04 在自编译的qt4.8.5界面程序在编译运行后无法显示中文
  4. 双时格林函数演练总结
  5. Java作业 折扣计算
  6. 青璃手游怎么用电脑玩 青璃手游模拟器玩法教程
  7. 定时器Timer与TimerTask的使用
  8. echarts 树图案例
  9. 程序员需要避免的致命错误
  10. 最新微信防红、防封技术系统原理