用户端web自动化测试-l1~l2--Loxida

一、SeleniumIDE用例录制

1、SeleniumIDE的下载以及安装

①官网:https://www.selenium.dev/
②Chrome插件:
https://chrome.google.com/webstore/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd
③Firefox插件:
https://addons.mozilla.org/en-US/firefox/addon/selenium-ide/
④github release:
https://github.com/SeleniumHQ/selenium-ide/releases
⑤其它版本:
https://addons.mozilla.org/en-GB/firefox/addon/selenium-ide/versions/ 
⑥注意:Chrome插件在国内无法下载,Firefox可以直接下载。

2、启动

1)安装完成后,通过在浏览器的菜单栏中点击它的图标来启动它:
2)如果没看到图标,首先确保是否安装了Selenium IDE扩展插件
3)通过以下链接访问所有插件
Chrome: chrome://extensions
Firefox: about:addons

3、SeleniumIDE常用功能

1)新建、保存、打开
2)开始和停止录制
3)运行8中的所有的实例
4)运行单个实例
5)调试模式
6)调整案例的运行速度
7)要录制的网址
8)实例列表
9)动作、目标、值
10)对单条命令的解释
11)运行日志

4、SeleniumIDE脚本导出:Java和Python

image

二、自动化测试用例结构分析

1、标准的用例结构(功能)

①用例标题
②前提条件
③用例步骤
④预期结果
⑤实际结果

2、自动化用例结构及作用

【结构:自动化测试用例:作用】
①用例标题:测试包、文件、类、方法名称:【用例的唯一标识】
②前提条件:setup、setup_class(Pytest;BeforeEach、BeforeAll(JUnit):【测试用例前的准备动作,比如读取数据或者driver的初始化】
③用例步骤:测试方法内的代码逻辑:【测试用例具体的步骤行为】
④预期结果:assert 实际结果 = 预期结果:【断言,印证用例是否执行成功】
⑤实际结果:assert 实际结果 = 预期结果:【断言,印证用例是否执行成功】
⑥后置动作:teardown、teardown_class(Pytest);@AfterEach、@AfterAll(JUnit):【脏数据清理、关闭driver进程】

3、IDE录制脚本

1)脚本步骤:
    ①访问百度网站
    ②搜索框输入“霍格沃兹测试开发”
    ③点击搜索按钮
# Generated by Selenium IDE
import pytest
import time
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

class Test():
  def setup_method(self, method):
    self.driver = webdriver.Chrome()
    self.vars = {}
  
  def teardown_method(self, method):
    self.driver.quit()
  
  def test_sougou(self):
    # 打开网页,设置窗口
    self.driver.get("https://www.sogou.com/")
    self.driver.set_window_size(1235, 693)
    # 输入搜索信息
    self.driver.find_element(By.ID, "query").click()
    self.driver.find_element(By.ID, "query").send_keys("霍格沃兹测试开发")
    # 点击搜索
    self.driver.find_element(By.ID, "stb").click()
    element = self.driver.find_element(By.ID, "stb")
    actions = ActionChains(self.driver)
    actions.move_to_element(element).perform()

#优化后的代码,添加断言判断用例是否成功
# Generated by Selenium IDE
import pytest
import time
import json
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities


class Test(object):
    # 前提条件,webdriver的初始化
    def setup_method(self, method):
        self.driver = webdriver.Chrome()
        self.vars = {}

    # 后置操作,关闭网站
    def teardown_method(self, method):
        self.driver.quit()

    # 测试用例步骤
    def test_sougou(self):
        # 打开网页,设置窗口
        self.driver.get("https://www.sogou.com/")
        self.driver.set_window_size(1235, 693)
        # 输入搜索信息
        self.driver.find_element(By.ID, "query").click()
        self.driver.find_element(By.ID, "query").send_keys("霍格沃兹测试开发")
        # 点击搜索
        self.driver.find_element(By.ID, "stb").click()
        element = self.driver.find_element(By.ID, "stb")
        actions = ActionChains(self.driver)
        actions.move_to_element(element).perform()
        # 问题:无法确定用例执行成功或失败
        # 解决方案:添加断言信息,判断搜索列表中,是否会有"霍格沃兹测试开发"
        res_element = self.driver.find_element(By.CSS_SELECTOR, "#sogou_vr_30000000_0 > em")
        # 获取到定位的文本信息
        # 判断实际获取到的搜索展示的列表和预期是否一致
        assert res_element.text == "霍格沃兹测试开发"

三、web 浏览器控制

  • 模拟功能测试中对浏览器的操作
操作 使用场景
get 打开浏览器 web自动化测试第一步
refresh 浏览器刷新 模拟浏览器刷新
back 浏览器退回 模拟退回步骤
maximize_window 最大化浏览器 模拟浏览器最大化
minimize_window 最小化浏览器 模拟浏览器最小化
import time
from selenium import webdriver
# 打开网页
def open_browser():
    # 实例化chromedriver
    driver = webdriver.Chrome()
    # 调用get方法时需要传递浏览器的URL
    driver.get("https://ceshiren.com/")
    # 添加等待1秒
    time.sleep(2)
    # # 刷新浏览器
    # driver.refresh()
    # # 通过get跳转到百度
    # driver.get("https://www.baidu.com/")
    # # 回退操作,退回到测试人网页
    # driver.back()
    # 最大化浏览器
    driver.maximize_window()
    time.sleep(2)
    # 最小化浏览器
    driver.minimize_window()
    time.sleep(2)

if __name__ == '__main__':
    open_browser()

四、常见控件定位方法

1、HTML铺垫

①标签:尖括号,如括起来的<a>、<html>、<title>等
②属性:a=b,如href
③类属性: class

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>测试人论坛</title>
</head>
<body>
<a href="https://ceshiren.com/" class="link">链接</a>
</body>
</html>

2、Selenium八大定位方式

1)selenium 常用定位方式
    #格式: 
    driver.find_element_by_定位方式(定位元素)
    driver.find_element(By.定位方式, 定位元素) 

    # 示例,两种方式作用一模一样
    # 官方建议使用下面的方式
    driver.find_element_by_id("su")
    driver.find_element(By.ID, "su") 
方式 描述
class name class 属性对应的值
css selector(重点) css 表达式
id(重点) id 属性对应的值
name(重点) name 属性对应的值
link text 查找其可见文本与搜索值匹配的锚元素
partial link text 查找其可见文本包含搜索值的锚元素。如果多个元素匹配,则只会选择第一个元素。
tag name 标签名称
xpath(重点) xpath表达式
2)【方式:描述】
①class name:class 属性对应的值
②css selector(重点):css 表达式
    a.格式: driver.find_element(By.CSS_SELECTOR, "css表达式")
    b.复制绝对定位
    c.编写 css selector 表达式
③id(重点):id 属性对应的值
    格式: driver.find_element(By.ID, "ID属性对应的值")
④name(重点):name 属性对应的值
    格式: driver.find_element(By.NAME, "Name属性对应的值")
⑤link text:查找其可见文本与搜索值匹配的锚元素
    格式:driver.find_element(By.LINK_TEXT,"文本信息")
⑥partial link text:查找其可见文本包含搜索值的锚元素。如果多个元素匹配,则只会选择第一个元素。
⑦tag name:标签名称
⑧xpath(重点):xpath表达式
    a.格式: driver.find_element(By.XPATH, "xpath表达式")
    b.复制绝对定位
    c.编写 xpath 表达式

from selenium import webdriver
from selenium.webdriver.common.by import By
def web_locate():
    # 首先需要实例化driver对象,Chrome一定要加括号
    driver = webdriver.Chrome()
    # 打开一个网页
    driver.get("https://vip.ceshiren.com/#/ui_study/frame")
    # 1.ID定位,第一个参数传递定位方式,第二个参数传递定位元素,调用这个方法的返回值为WebElement
    web_element = driver.find_element(By.ID, "locate_id")
    print(web_element)
    # 2.NAME定位,如果没有报错,证明元素找到了
    # 如果报错no such element,代表元素定位可能出现错误
    # driver.find_element(By.NAME, "locate123")   # 错误示例
    driver.find_element(By.NAME, "locate")
    # 3.CSS选择器定位
    driver.find_element(By.CSS_SELECTOR, "#locate_id > a > span")
    # 4.xpath表达式定位
    driver.find_element(By.XPATH, '//*[@id="locate_id"]/a/span')  # 表达式里有双引号,建议用单引号
    # 5.Link text,通过链接文本的方式,(1)元素一定是a标签;(2)输入的元素为标签内的文本
    driver.find_element(By.LINK_TEXT, "元素定位")   # 通常会带一个点击的操作,在最后面添加.click(),可加可不加


if __name__ == '__main__':
    web_locate()

五、强制等待与隐式等待

1、为什么要添加等待

避免页面未渲染完成后操作,导致的报错
from selenium import webdriver
from selenium.webdriver.common.by import By

def wait_sleep():
    """
    如果直接执行,不添加任何等待,可能会报错
    """
    driver = webdriver.Chrome()
    driver.get("https://vip.ceshiren.com/")
    # 不加等待,可能会因为网速等原因产生报错
    # 报错:no such element: Unable to locate element
    # 原因:页面未加载完成,就去查找元素,此时这个元素还未加载出来
    driver.find_element(By.XPATH, "//*[text()='个人中心']")

if __name__ == '__main__':
    wait_sleep()

2、强制(直接)等待

(1)解决方案:在报错的元素操作之前添加等待
(2)原理:强制等待,线程休眠一定时间。time.sleep(3)
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

def wait_sleep():
    """
    如果直接执行,不添加任何等待,可能会报错
    """
    driver = webdriver.Chrome()
    driver.get("https://vip.ceshiren.com/")
    # 不加等待,可能会因为网速等原因产生报错
    # 报错:no such element: Unable to locate element
    # 原因:页面未加载完成,就去查找元素,此时这个元素还未加载出来
    # 1.强制等待,让页面渲染完成,在报错的元素操作之前添加等待,没有报错,就证明是页面渲染速度导致,有报错则是其他问题,如定位错误等
    time.sleep(3)
    driver.find_element(By.XPATH, "//*[text()='个人中心']")

if __name__ == '__main__':
    wait_sleep()

3、隐式等待

(1)问题:难以确定元素加载的具体等待时间。
(2)解决方案:针对于寻找元素的这个动作,使用隐式等待添加配置。
(3)原理:设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

def wait_sleep():
    """
    如果直接执行,不添加任何等待,可能会报错
    """
    driver = webdriver.Chrome()
    driver.get("https://vip.ceshiren.com/")
    # 不加等待,可能会因为网速等原因产生报错
    # 报错:no such element: Unable to locate element
    # 原因:页面未加载完成,就去查找元素,此时这个元素还未加载出来
    # 1.强制等待,让页面渲染完成,在报错的元素操作之前添加等待,没有报错,就证明是页面渲染速度导致,有报错则是其他问题,如定位错误等
    # time.sleep(3)
    # 强制等待的问题:(1)不确定页面加载时间,可能会因为等待时间过长,而影响用例的执行效率;(2)不确定页面加载时间,可能会因为等待时间过短,而导致代码依然报错
    # 2.隐式等待
    # 设置一个最长的等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
    # 注意:(1)在代码一开始运行时就添加隐式等待的配置,隐式等待是全局生效,即在所有find_element动作之前添加该配置即可;(2)隐式等待只能解决元素查找问题,不能解决元素交互问题
    driver.implicitly_wait(3)
    driver.find_element(By.XPATH, "//*[text()='个人中心']")
    # driver.implicitly_wait(5)    # 修改隐式等待的配置
    driver.find_element(By.XPATH, "//*[text()='题库']")

4、隐式等待无法解决的问题

(1)问题:元素可以找到,使用点击等操作,出现报错
(2)原因:
①页面元素加载是异步加载过程,通常html会先加载完成,js、css其后
②元素存在与否是由HTML决定,元素的交互是由css或者js决定
③隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互
(3)解决方案:使用显式等待

5、显式等待基本使用(初级)

(1)示例: WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)
(2)原理:在最长等待时间内,轮询,是否满足结束条件
注意:在初级时期,先关注使用
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


"""显示等待"""


def wait_show():
    driver = webdriver.Chrome()
    driver.get("https://vip.ceshiren.com/#/ui_study/frame")
    # driver.implicitly_wait(3)  # 问题:元素可以找到,但是点击效果没有触发
    # 显示等待,第一个参数是driver,第二个参数是最长等待时间,轮询时间可加可不加,util方法内需要结合expected_conditions或者自己封装的方法进行使用
    # expected_conditions的参数传入都是一个元组,即多一层小括号
    WebDriverWait(driver, 10).until(expected_conditions.element_to_be_clickable((By.ID, "success_btn")))
    driver.find_element(By.ID, "success_btn").click()   # 添加点击操作.click(),点击"消息提示"
    time.sleep(5)


if __name__ == '__main__':
    wait_show()

6、总结

(1)直接等待:
    1)使用方式:time.sleep(等待时间))
    2)原理:强制线程等待
    3)适用场景:调试代码,临时性添加
(2)隐式等待:
    1)使用方式:driver.implicitly_wait(等待时间)
    2)原理:在时间范围内,轮询查找元素
    3)适用场景:解决找不到元素问题,无法解决交互问题
(3)显式等待:
    1)使用方式:WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)
    2)原理:设定特定的等待条件,轮询操作
    3)适用场景:解决特定条件下的等待问题,比如点击等交互性行为

六、常见控件交互方法

1、元素操作

点击,输入,清空
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

def element_interaction():
    """
    元素操作:点击、输入、清空
    :return:
    """
    # 1.实例化driver对象
    driver = webdriver.Chrome()
    # 2.打开一个网页
    driver.get("https://www.sogou.com/")
    # 3.定位到输入框进行输入操作,.send_keys()
    driver.find_element(By.ID, "query").send_keys("霍格沃滋测试开发")
    # 强制等待2秒
    time.sleep(2)
    # 4.对输入框进行清空操作.clear()
    driver.find_element(By.ID, "query").clear()
    time.sleep(2)
    # 5.再次输入
    driver.find_element(By.ID, "query").send_keys("霍格沃滋测试开发学社")
    time.sleep(2)
    # 6.点击搜索.click()
    driver.find_element(By.ID, "stb").click()
    time.sleep(2)

if __name__ == '__main__':
    element_interaction()

2、获取元素属性信息

①原因:定位到元素后,获取元素的文本信息,属性信息等
②目的:根据这些信息进行断言或者调试
import time
from selenium import webdriver
from selenium.webdriver.common.by import By

# 获取元素属性
def element_get_attr():
    # 1.实例化driver对象
    driver = webdriver.Chrome()
    # 2.打开网页
    driver.get("https://vip.ceshiren.com/#/ui_study/iframe")
    # 3.定位一个元素
    web_element = driver.find_element(By.ID, "locate_id")
    # 4.打印这个元素信息
    # 使用debug方式查看,断点打在想看的对象的下一行
    # print(web_element)
    # 5.获取元素的文本信息,不是每个元素都有文本信息的
    print(web_element.text)
    # 6.获取元素的属性信息,如 id="locate_id", title="xxx"
    res = web_element.get_attribute("class")
    print(res)

if __name__ == '__main__':
    # 获取元素属性
    element_get_attr()

3、获取元素属性信息的方法

①获取元素文本
②获取元素的属性(html的属性值)

# 获取元素文本
driver.find_element(By.ID, "id").text
# 获取这个元素的name属性的值
driver.find_element(By.ID, "id").get_attribute("name")

七、自动化测试定位策略

1、定位方式

2、通用 Web 定位方式

定位策略 描述
class name 通过 class 属性定位元素
css selector 通过匹配 css selector 定位元素
id 通过 id 属性匹配元素
name 通过 name 属性定位元素
link text 通过 text 标签中间的 text 文本定位元素
partial link text 通过 text 标签中间的 text 文本的部分内容定位元素
tag name 通过 tag 名称定位元素
xpath 通过 xpath 表达式匹配元素
【定位策略:描述】
①class name:通过 class 属性定位元素
②css selector:通过匹配 css selector 定位元素
③id:通过 id 属性匹配元素
④name:通过 name 属性定位元素
⑤link text:通过 <a>text</a> 标签中间的 text 文本定位元素
⑥partial link text:通过 <a>text</a> 标签中间的 text 文本的部分内容定位元素
⑦tag name:通过 tag 名称定位元素
⑧xpath:通过 xpath 表达式匹配元素

3、选择定位器通用原则

1)与研发约定的属性优先(class属性: [name='locate'])
2)身份属性 id,name(web 定位)
3)复杂场景使用组合定位:
    ①xpath,css
    ②属性动态变化(id,text)
    ③重复元素属性(id,text,class)
    ④父子定位(子定位父)
4)js定位

4、Web 弹框定位

1)场景:web 页面 alert 弹框
2)解决:web 需要使用 driver.switchTo().alert() 处理

5、下拉框/日期控件定位

1)场景:
<input>标签组合的下拉框无法定位
<input>标签组合的日期控件无法定位
2)解决:面对这些元素,我们可以引入JS注入技术来解决问题。

6、文件上传定位

1)场景:input 标签文件上传
2)解决:input 标签直接使用 send_keys()方法

L1实战–测试人论坛搜索功能自动化测试

from selenium import webdriver
from selenium.webdriver.common.by import By

class TestCeshiren01:

    def setup(self):
        """
        前提条件:进入测试人论坛的搜索页面
        :return:
        """
        # 实例化driver对象
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)  # 隐式等待
        # 打开被测地址
        self.driver.get("https://ceshiren.com/search?expanded=true")

    def teardown(self):
        # 每一次用例结束之后都会关闭chromedriver进程,也会关闭浏览器
        self.driver.quit()

    def test_search01(self):
        """
        测试步骤:1.输入搜索关键词;2.点击搜索按钮
        :return:
        """
        # 定位搜索框,并输入搜索内容,如果是动态id,使用css
        self.driver.find_element(By.CSS_SELECTOR, "[placeholder=搜索]").send_keys("appium")
        # 定位到搜索按钮,并点击搜索
        self.driver.find_element(By.CSS_SELECTOR, ".search-cta").click()
        # 断言=预期结果与实际结果对比的结果
        # 定位实际结果,即为获取搜索结果列表的标题内容
        web_element = self.driver.find_element(By.CSS_SELECTOR, ".topic-title")
        # 获取文本类的实际结果断言,appium关键字是否在获取的实际结果文本之中
        assert "appium" in web_element.text

八、高级定位-css

1、css 选择器概念

①css 选择器有自己的语法规则和表达式
②css 定位通常分为绝对定位和相对定位
③和Xpath一起常用于UI自动化测试中的元素定位

2、css 相对定位使用场景

①支持web产品
②支持app端的webview

3、css 相对定位的优点

①可维护性更强
②语法更加简洁
③解决各种复杂的定位场景

4、css 定位的调试方法

1)进入浏览器的console
2)输入:$("css表达式")或者$$("css表达式"),如果表达式里有双引号,外面要单引号,如果表达式用的是单引号,外面就要用双引号,外双内单,外单内双

5、css基础语法

类型 表达式
标签 标签名
. (表示class属性值)
ID # (表示id属性值)
属性 [属性名=‘属性值’]
【类型:表达式】
①标签:标签名
    $("div")  # 获取所有的div标签
②类:.表示为class属性值,如果类里面有多个值(单词),就不能直接复制,将空格改为.即可
    $(".logo-big")等同于$("[class='logo-big']")
③ID:#表示为id属性值
    $("#site-logo")等同于$("[id='site-logo']")
④属性:[属性名='属性值']
    $("[alt='测试人社区']")

6、css关系定位

类型 格式
并集 元素,元素
邻近兄弟(了解即可) 元素+元素
兄弟(了解即可) 元素1~元素2
父子 元素>元素
后代 元素 元素
【类型:格式】
①并集:元素,元素
    $("#main,#ember4")

②邻近兄弟(了解即可):元素+元素
    $("#ember39+#ember40")
③兄弟(了解即可):元素1~元素2
    $("#ember39~#ember41")
④父子:元素>元素
    $("#main>#ember4")
⑤后代:元素 元素
    $("#main #skip-link")

7、css 顺序关系

类型 格式
父子关系+顺序 元素 元素
父子关系+标签类型+顺序 元素 元素
【类型:格式】
①父子关系+顺序:元素 元素(父亲有多个孩子,找到第一个孩子)
    表达式://:nth-child(n)
    $("#ember15>:nth-child(3)")  ( #ember15为父亲,:nth-child(3)为第三个孩子)
②父子关系+标签类型+顺序:元素 元素(父亲的孩子有多个类型,想找其中一个类型里面的第一个孩子)
    表达式://:nth-of-type(n)
    $("#ember15>div:nth-of-type(1)")( #ember15为父亲,:nth-of-type(1)为第三个类型div的第一个孩子)

css表达式定位:

image

九、高级定位-xpath

1、xpath 基本概念

①XPath 是一门在 XML 文档中查找信息的语言
②XPath 使用路径表达式在 XML 文档中进行导航
③XPath 的应用非常广泛
④XPath 可以应用在UI自动化测试

2、xpath 使用场景

①web自动化测试
②app自动化测试

3、xpath 相对定位的优点

①可维护性更强
②语法更加简洁
③相比于css可以支持更多的方式

4、xpath 定位的调试方法

1)浏览器-console
    $x("xpath表达式")
2)浏览器-elements
    ctrl+f 输入xpath或者css

5、xpath 基础语法(包含关系)

表达式 结果
/ 从该节点的子元素选取
// 从该节点的子孙元素选取
* 通配符
nodename 选取此节点的所有子节点
选取当前节点的父节点
@ 选取属性
【表达式:结果】
①/ :从该节点的子元素选取
②// :从该节点的子孙元素选取
③* :通配符
$x("//*[@id='ember61']"),匹配所有的[@id='ember61']
$x("//tr[@id='ember61']"),匹配tr标签下面的[@id='ember61']
④nodename :选取此节点的所有子节点,相当于标签名
⑤.. :选取当前节点的父节点,
如$x("//*[@id='ember61']/.."),寻找ember61的父节点
写法二:$x("//tr[@id='ember61']"),在某个标签下寻找
⑥@ :选取属性

# 整个页面
$x("/")

image

# 页面中的所有的子元素,匹配/下面的所有节点,相当于是html
$x("/*")

# 整个页面中的所有元素
$x("//*")

image

# 查找页面上面所有的div标签节点,标签不等于属性
$x("//div")

image

# 查找id属性为ember61的节点,*先匹配所有节点,再匹配某个属性
$x('//*[@id="ember61"]')

# 查找ember61节点的父节点
$x('//*[@id="ember61"]/..')
$x("//*[@id='ember61']/../..")   # 再往上寻找父节点

6、xpath 顺序关系(索引)

xpath通过索引直接获取对应元素

# 查找tbody下的所有tr
$x("//tbody//tr")
# 查找tbody下的第一个tr,下标从1开始
$x("//tbody//tr[1]")
# 获取所有的tr,注意这里的tr并不是都是同一个父节点
$x("//tr")
# 获取每个不同父节点下面的第一个tr
$x("//tr[1]")

7、xpath 高级用法

①[last()]: 选取最后一个
$x("//tr[last()]")   # 获取每一个父节点下的最后一个tr
$x("//tbody//tr[last()]")   # 获取tbody下面的最后一个tr

②[@属性名='属性值' and @属性名='属性值']: 与关系
$x("//*[@class='topic-list-item category-bu-161-category unseen-topic ember-view' and @id='ember44']")

③[@属性名='属性值' or @属性名='属性值']: 或关系
$x("//*[@class='topic-list-item category-bu-161-category unseen-topic ember-view' or @id='ember44']")

④[text()='文本信息']: 根据文本信息定位
$x("//*[text()='赏金任务']")   # text不是属性,不需要加@,是一个方法,加括号

⑤[contains(text(),'文本信息')]: 根据文本信息包含定位;也可以contains(@id或@name等)
$x("//*[contains(text(),'赏金')]")
$x("//*[contains(@id,'site')]")

⑥注意:所有的表达式需要和[]结合

十、显式等待高级使用

1、显式等待原理

①在代码中定义等待一定条件发生后再进一步执行代码
②在最长等待时间内循环执行结束条件的函数,结合③一起查看
③源码:WebDriverWait(driver 实例, 最长等待时间, 轮询时间).until(结束条件函数)

2、常见 expected_conditions

类型 示例方法 说明
element element_to_be_clickable();visibility_of_element_located() 针对于元素,比如判断元素是否可以点击,或者元素是否可见
url url_contains() 针对于 url
title title_is() 针对于标题
frame frame_to_be_available_and_switch_to_it(locator) 针对于 frame
alert alert_is_present() 针对于弹窗
1)类型:element:
    示例方法:
    element_to_be_clickable()visibility_of_element_located()
    说明:针对于元素,比如判断元素是否可以点击,或者元素是否可见
2)类型:url
    示例方法:url_contains()
    说明:针对于 url
3)类型:title
    示例方法:title_is()
    说明:针对于标题
4)类型:frame
    示例方法:frame_to_be_available_and_switch_to_it(locator)
    说明:针对于 frame
5)类型:alert	
    示例方法:alert_is_present()
    说明:针对于弹窗

3、封装等待条件

1)官方的 excepted_conditions 不可能覆盖所有场景,如有些按钮需要点击两次或多次才会有反应
2)定制封装条件会更加灵活、可控
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait


class TestWebdriverWait:

    driver = webdriver.Chrome()

    driver.maximize_window()
    driver.implicitly_wait(5)
    driver.get("https://vip.ceshiren.com/#/ui_study/iframe")
    def teardown(self):
        self.driver.quit()
    def test_webdriver_wait(self):
        # 解决的问题:有的按钮点击一次没有反应,可能要点击多次,比如企业微信的添加成员
        # 解决的方案:一直点击按钮,直到下个页面出现,封装成显式等待的一个条件
        def muliti_click(button_element,until_ele):
            # 函数封装
            def inner(driver):
                # 封装点击方法
                driver.find_element(By.XPATH,button_element).click()
                return driver.find_element(By.XPATH,until_ele)
            return inner
        time.sleep(5)
        # 在限制时间内会一直点击按钮,直到展示弹框
        WebDriverWait(self.driver,10).until(muliti_click("//*[text()='点击两次响应']","//*[text()='该弹框点击两次后才会弹出']"))
        time.sleep(5)

十一、高级控件交互方法

1、使用场景

使用场景 对应事件
复制粘贴 键盘事件
拖动元素到某个位置 鼠标事件
鼠标悬停 鼠标事件
滚动到某个元素 滚动事件
使用触控笔点击 触控笔事件(了解即可)
1)复制粘贴
对应事件:键盘事件
2)拖动元素到某个位置
对应事件:鼠标事件
3)鼠标悬停
对应事件:鼠标事件
4)滚动到某个元素
对应事件:滚动事件
5)使用触控笔点击
对应事件:触控笔事件(了解即可)

2、ActionChains解析

1)实例化类ActionChains,参数为driver实例。
2)中间可以有多个操作。
3).perform()代表确定执行。

3、键盘事件

1)按下、释放键盘键位
2)结合send_keys回车
3)键盘事件-使用shift实现大写
    ①ActionChains(self.driver): 实例化ActionChains类
    ②key_down(Keys.SHIFT, ele): 按下shift键实现大写
    ③send_keys("selenium"): 输入大写的selenium
    ④perform(): 确认执行
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

class TestKeyBoard:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)  # 隐式等待3秒

    def teardown_class(self):
        self.driver.quit()

    def test_shift(self):
        """
	     键盘事件-使用shift实现大写
        1.访问https://ceshiren.com/官方网站
        2.点击搜索按钮
        3.输入搜索的内容,输入的同时按着shift键
        :return:
        """
        self.driver.get("https://ceshiren.com/")
        # 点击搜索按钮
        self.driver.find_element(By.ID, "search-button").click()
        # 目标元素,即为输入框
        ele = self.driver.find_element(By.ID, "search-term")
        # key_down代表按下某个键位,.send_keys输入内容,.perform()确认执行以上操作
        ActionChains(self.driver).key_down(Keys.SHIFT, ele).send_keys("selenium").perform()
4)键盘事件-输入后回车
    ①直接输入回车: 元素.send_keys(Keys.ENTER)
    ②使用ActionChains: key_down(Keys.ENTER)
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

class TestKeyBoard:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)  # 隐式等待3秒

    def teardown_class(self):
        self.driver.quit()

    def test_enter(self):
	 """键盘事件-输入后回车"""
        self.driver.get("https://www.sogou.com/")
        self.driver.find_element(By.ID, "query").send_keys("python语言")
        # 第一种回车方式,直接输入回车
        self.driver.find_element(By.ID, "query").send_keys(Keys.ENTER)
        # 第二种使用ActionChains,要记得加.perform()
        ActionChains(self.driver).key_down(Keys.ENTER).perform()
5)键盘事件-复制粘贴
    ①多系统兼容
    mac 的复制按钮为 COMMAND
    windows 的复制按钮为 CONTROL
    ②左箭头:Keys.ARROW_LEFT
    ③按下COMMAND或者CONTROL: key_down(cmd_ctrl)
    ④按下剪切与粘贴按钮: send_keys("xvvvvv")
import sys
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

class TestKeyBoard:
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)  # 隐式等待3秒

    def teardown_class(self):
        self.driver.quit()

    def test_copy_and_paste(self):
        """键盘事件-复制粘贴"""
        # 打开网页
        self.driver.get("https://ceshiren.com/")
        # 点击搜索按钮
        self.driver.find_element(By.ID, "search-button").click()
        # 目标元素,即为输入框
        ele = self.driver.find_element(By.ID, "search-term")
        # 判断操作系统是否为Mac(darwin),是mac返回command键位,windows返回control键位
        command_control = Keys.COMMAND if sys.platform == "darwin" else Keys.CONTROL
        # .key_down(Keys.SHIFT, ele)按下shift键,.send_keys("selenium")输入大写selenium,
        # .key_down(Keys.ARROW_LEFT)按下左箭头,.key_down(command_control)按下command或control键位,
        # .send_keys("xvv"),x表示为剪切,多少个v表示复制多少次,.key_up(command_control)表示松开command或control键位,.perform()执行
        ActionChains(self.driver)\
            .key_down(Keys.SHIFT, ele)\
            .send_keys("selenium@")\
            .key_down(Keys.ARROW_LEFT)\
            .key_down(command_control).send_keys("xvvvvvv").key_up(command_control)\
            .perform()

4、鼠标事件

1)双击
    double_click(元素对象): 双击元素
2)拖动元素
    drag_and_drop(起始元素对象, 结束元素对象): 拖动并放开元素
3)指定位置(悬浮)
    move_to_element(元素对象): 移动到某个元素
import time
from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By

class TestKeyBoard:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)  # 隐式等待3秒

    def teardown_class(self):
        self.driver.quit()

    def test_double_click(self):
        """鼠标事件-双击"""
        # 打开网页
        self.driver.get("https://vip.ceshiren.com/#/ui_study/iframe")
        # 获取点击按钮
        ele = self.driver.find_element(By.ID, "primary_btn")
        # .double_click(ele)调用双击方法,传入双击元素,.perform()执行
        ActionChains(self.driver).double_click(ele).perform()

    def test_drag_and_drop(self):
        """鼠标事件-拖动元素"""
        # 打开网页
        self.driver.get("https://vip.ceshiren.com/#/ui_study/action_chains")
        # 获取需要拖动的元素,即起始元素的位置
        item_left = self.driver.find_element(By.CSS_SELECTOR, '#item1')   # #表示id标签
        # 获取目标元素的位置
        item_right = self.driver.find_element(By.CSS_SELECTOR, '#item3')  # #表示id标签
        # 实现拖拽操作,.drag_and_drop(item_left, item_right)
        ActionChains(self.driver).drag_and_drop(item_left, item_right).perform()

    def test_hover(self):
        """鼠标事件-悬浮"""
        self.driver.get("https://vip.ceshiren.com/#/ui_study/action_chains2")
        # 获取下拉框位置
        ele = self.driver.find_element(By.CSS_SELECTOR, ".menu")
        # 鼠标悬浮在下拉框
        ActionChains(self.driver).move_to_element(ele).perform()
        # 选择下拉选项
        self.driver.find_element(By.XPATH, "//*[contains(text(),'测开班')]").click()
        time.sleep(3)

5、滚轮/滚动操作

1)滚动到元素
    scroll_to_element(WebElement对象):滚动到某个元素
2)根据坐标滚动
    scroll_by_amount(横坐标, 纵坐标)
3)注意: selenium 版本需要在 4.2 之后
import time
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

class TestScroll:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(10)  # 隐式等待

    def teardown_class(self):
        self.driver.quit()

    def test_scoll_to_element(self):
        """滚轮/滚动操作-滚动到元素"""
        # 打开网页
        self.driver.get("https://ceshiren.com/")
        # 获取页面底部某个标题
        ele = self.driver.find_element(By.XPATH, "//*[text()='接口测试上线标准']")
        # 页面滚动操作,找到"接口测试上线标准",selenium版本需要在 4.2 之后
        ActionChains(self.driver).scroll_to_element(ele).perform()
        time.sleep(5)

    def test_scroll_to_xy(self):
        """滚轮/滚动操作-根据坐标滚动"""
        self.driver.get("https://ceshiren.com/")
        # 坐标滚动方式,.scroll_by_amount(0, 10000)纵向滚动
        ActionChains(self.driver).scroll_by_amount(0, 3000).perform()
        time.sleep(3)

十二、网页 frame 与多窗口处理

1、selenium⾥⾯如何处理多窗⼜场景

1) 多个窗口识别
2)多个窗口之间切换

2、selenium⾥⾯如何处理frame

1)多个frame识别
2)多个frame之间切换

3、多窗口处理

1)点击某些链接,会重新打开⼀个窗口,对于这种情况,想在新页⾯上操作,就
得先切换窗口了。
2)获取窗口的唯⼀标识⽤句柄表⽰,所以只需要切换句柄,就可以在多个页⾯灵
活操作了。

4、多窗口处理流程

1)先获取到当前的窗口句柄(driver.current_window_handle)
2)再获取到所有的窗口句柄(driver.window_handles)
3.)判断是否是想要操作的窗口,如果是,就可以对窗口进⾏操作,如果不是,
跳转到另外⼀个窗口,对另⼀个窗口进⾏操作 (driver.switch_to_window)
"""base.py文件,Base类有前置和后置操作"""
from selenium import webdriver

# 前置和后置操作,提供测试用例调用
class Base(object):
    # 前置操作
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(5)  # 隐式等待

    # 后置操作
    def teardown(self):
        self.driver.quit()


"""测试用例文件test_window.py"""
import time
from selenium.webdriver.common.by import By
from webui.window_frame.base import Base


# 继承Base类,Base类里有前置和后置操作
class TestWindows(Base):
    def test_window(self):
        self.driver.get("https://www.baidu.com/")
        # 找到登录按钮,并点击
        self.driver.find_element(By.XPATH, "//*[@id='s-top-loginbtn']").click()
        print(self.driver.current_window_handle)   # 点击登录之后,打印当前窗口
        # 点击弹窗里的立即注册按钮
        self.driver.find_element(By.XPATH, "//*[text()='立即注册']").click()
        # # 点击立即注册之后,打印当前窗口
        # print(self.driver.current_window_handle)
        # # 打印所有窗口的名字,列表形式,会有注册页面的窗口,
        # print(self.driver.window_handles)
        # 获取所有窗口列表,赋给window
        window = self.driver.window_handles
        # window[-1]获取注册页面的窗口,并切换到注册页面的窗口
        self.driver.switch_to.window(window[-1])
        # 注册页面,输入用户名username
        self.driver.find_element(By.ID, "TANGRAM__PSP_4__userName").send_keys("username")
        time.sleep(3)
        # 切换回最初登录页面的窗口
        self.driver.switch_to.window(window[0])
        # # 登录页面,点击短信登录
        # self.driver.find_element(By.XPATH, "//*[text()='短信登录']").click()
        # 获取用户名输入框,并输入用户名
        self.driver.find_element(By.ID, "TANGRAM__PSP_11__userName").send_keys("login_username")
        # 获取密码输入框,并输入密码
        self.driver.find_element(By.ID, "TANGRAM__PSP_11__password").send_keys("login_password")
        # 点击登录
        self.driver.find_element(By.ID, "TANGRAM__PSP_11__submit").click()
        time.sleep(6)

5、frame处理

1)frame介绍
    ①在web⾃动化中,如果⼀个元素定位不到,那么很⼤可能是在iframe中。
    ②什么是frame?
        a. frame是html中的框架,在html中,所谓的框架就是可以在同⼀个浏览器中显⽰不⽌⼀个页⾯。
        b. 基于html的框架,又分为垂直框架和⽔平框架(cols,rows)
    ③Frame 分类
        a. frame标签包含frameset、frame、iframe三种,
        b. frameset和普通的标签⼀样,不会影响正常的定位,可以使⽤index、id、name 、webelement任意种⽅式定位frame。
        c. ⽽frame与iframe对selenium定位⽽⾔是⼀样的。selenium有⼀组⽅法对frame进⾏操作

2)多frame切换
    ①frame存在两种:⼀种是嵌套的,⼀种是未嵌套的
    ②切换frame
        ❖ driver.switch_to.frame() # 根据元素id或者index切换切换frame
        ❖ driver.switch_to.default_content() # 切换到默认frame
        ❖ driver.switch_to.parent_frame() # 切换到⽗级frame

3)frame未嵌套
    ①处理未嵌套的iframe
        ❖ driver.switch_to_frame(“frame 的 id”)
        ❖ driver.switch_to_frame(“frame - index”) frame⽆ID的时候依据索引来处理,索引从0开始 driver.switch_to_frame(0)

4)frame嵌套
    ②处理嵌套的iframe
        ❖ 对于嵌套的先进⼊到iframe的⽗节点,再进到⼦节点,然后可以对⼦节点⾥⾯的对象进⾏处理和操作
        ❖ driver.switch_to.frame(“⽗节点”)
        ❖ driver.switch_to.frame(“⼦节点”)
import time
from selenium.webdriver.common.by import By
from webui.window_frame.base import Base

# 继承Base类,Base类里有前置和后置操作
class TestFrame(Base):
    def test_frame(self):
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        # 切换到frame中,根据frame的元素id
        self.driver.switch_to.frame("iframeResult")  # 写法一,常用
        # self.driver.switch_to_frame("iframeResult")  # 写法二
        # 获取"请拖拽我!"的属性,获取文本内容
        print(self.driver.find_element(By.ID, "draggable").text)

        # 写法一:切回网页,即不在frame页面,即切回frame父节点
        # self.driver.switch_to.parent_frame()
        # 写法二:切换到默认frame节点,即刚打开的地址节点
        self.driver.switch_to.default_content()
        # 获取"点击运行"按钮,并点击
        self.driver.find_element(By.ID, "submitBTN").click()
        time.sleep(5)

十三、文件上传弹框处理

1、⽂件上传

1)input标签可以直接使⽤send_keys(⽂件地址)上传⽂件
2) ⽤法:
    ①el = driver.find_element_by_id('上传按钮id')
    ②el.send_keys(”⽂件路径+⽂件名")
import time
from selenium.webdriver.common.by import By
from webui.window_frame.base import Base

# 继承Base类,Base类里有前置和后置操作
class TestFile(Base):
    def test_file_upload(self):
        self.driver.get("https://www.baidu.com/")
        # 找到百度的相机图标并点击
        self.driver.find_element(By.XPATH, "//*[@class='soutu-btn']").click()
        # 定位到选择文件,并选择文件
        self.driver.find_element(By.XPATH, "//input[@class='upload-pic']").send_keys("/Users/jiyu/PycharmProjects/pythonProject/webui/img/12.png")
        time.sleep(5)

2、chrome 开启 debug 模式

1)有时候登录⽅式⽐较繁琐,需要动态⼿机密码,⼆维码登录之类的。⾃动话实现⽐较⿇烦。⼿⼯登录后,
不想让selenium启动⼀个新浏览器。可以使⽤chrome的debug⽅式来执⾏测试。
2)启动chrome的时候需要先退出所有chrome进程。使⽤ps aux|grep chrome|grep -v 'grep'查看是否有
chrome进程存在。确保没有chrome进程被启动过。
3)正常启动chrome的debug模式
    ①# 默认macOS系统
    /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debuggingport=9222
    ②# Windows下找到chrome.exe位置执⾏下⾯的命令
    chrome.exe --remote-debugging-port=9222
4)启动后的提⽰信息,代表chrome运⾏正常,不要关闭⾃动打开的chrome窗口。

3、弹框处理机制

1)在页⾯操作中有时会遇到JavaScript所⽣成的alert、confirm以及prompt弹框,可以使⽤switch_to.alert()⽅法定位到。然后使⽤text/accept/dismiss/send_keys等⽅法进⾏操作。
2)操作alert常⽤的⽅法:
    ①switch_to.alert():获取当前页⾯上的警告框。
    ②text:返回alert/confirm/prompt 中的⽂字信息。
    ③accept():接受现有警告框。
    ④dismiss():解散现有警告框。
    ⑤send_keys(keysToSend):发送⽂本⾄警告框。keysToSend:将⽂本发送⾄警告框。

import time
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from webui.window_frame.base import Base

class TestAlert(Base):
    def test_alert(self):
        """
        ❖ 打开⽹页 
https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
        ❖ 操作窗⼜右侧页⾯, 将元素1拖拽到元素2
        ❖ 这时候会有⼀个alert弹框,点击弹框中的’确定’
        ❖ 然后再按’点击运⾏’
        ❖ 关闭⽹页
        :return:
        """
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        # 切换到frame中,根据frame的元素id
        self.driver.switch_to.frame("iframeResult")
        # 获取需要拖动的元素,即起始元素的位置
        a1 = self.driver.find_element(By.CSS_SELECTOR, '#draggable')
        # 获取目标元素的位置
        a2 = self.driver.find_element(By.CSS_SELECTOR, '#droppable')
        # 实现拖拽操作,.drag_and_drop(a1, a2):将a1拖拽到a2上,.perform()执行
        action = ActionChains(self.driver)
        action.drag_and_drop(a1, a2).perform()
        time.sleep(2)  # 强制等待2秒,查看效果
        # 切换到alert页面,点击alert的确认按钮.accept()
        self.driver.switch_to.alert.accept()
        # 退出alert页面,返回到默认frame节点
        self.driver.switch_to.default_content()
        # 获取"点击运行"按钮,并点击
        self.driver.find_element(By.ID, 'submitBTN').click()
        time.sleep(3)

十四、自动化关键数据记录

1、什么是关键数据

①代码的执行日志
②代码执行的截图
③page source(页面源代码)

2、记录关键数据的作用

内容 作用
日志 1. 记录代码的执行记录,方便复现场景;2. 可以作为bug依据
截图 1. 断言失败或成功截图;2.异常截图达到丰富报告的作用;3. 可以作为bug依据
page source 1. 协助排查报错时元素当时是否存在页面上
1)日志:
    ①记录代码的执行记录,方便复现场景
    ②可以作为bug依据
2)截图
    ①断言失败或成功截图
    ②异常截图达到丰富报告的作用
    ③可以作为bug依据
3)page source
    ①协助排查报错时元素当时是否存在页面上

3、行为日志记录

1)日志配置
2)脚本日志级别
    ①debug记录步骤信息
    ②info记录关键信息,比如断言等
#日志配置文件log_utils.py
# 日志配置
import logging
# 创建logger实例
logger = logging.getLogger('simple_example')
# 设置日志级别
logger.setLevel(logging.DEBUG)
# 流处理器
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# 日志打印格式
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 添加格式配置
ch.setFormatter(formatter)
# 添加日志配置
logger.addHandler(ch)
# 测试用例脚本文件test_data_record.py
# 日志与脚本结合
from selenium import webdriver
from selenium.webdriver.common.by import By
from webui.data_record.log_utils import logger
from webui.window_frame.base import Base

# 继承Base类,Base类里有前置和后置操作
class TestDataRecord(Base):
    """"""
    def test_data_record(self):
        search_content = "python语言"
        # 打开网页
        self.driver.get("https://www.sogou.com/")
        # 定位输入框,输入内容
        self.driver.find_element(By.ID, "query").send_keys(search_content)
        # 加入日志,步骤信息较多且琐碎,使用debug
        logger.debug(f"搜索的信息为:{search_content}")
        # 点击搜索
        self.driver.find_element(By.ID, "stb").click()
        # 获取搜索结果列表的标题,对应测试结果的实际结果
        search_result = self.driver.find_element(By.CSS_SELECTOR, "em")  # 多个em的情况下,默认拿第一个
        # 添加日志,断言使用info,级别高于debug,断言实际结果与预期结果是否一致
        logger.info(f"实际结果为:{search_result.text},预期结果为:{search_content}")
        # 断言,search_result.text 获取搜索结果的文本信息,预期结果search_content
        assert search_result.text == search_content

4、步骤截图记录

1)save_screenshot(截图路径+名称)
2)记录关键页面
    ①断言页面
    ②重要的业务场景页面
    ③容易出错的页面
from selenium.webdriver.common.by import By
from webui.data_record.log_utils import logger
from webui.window_frame.base import Base

# 继承Base类,Base类里有前置和后置操作
class TestDataRecord(Base):
    def test_screen_shot_data_record(self):
        search_content01 = "python语言"
        # 打开网页
        self.driver.get("https://www.sogou.com/")
        # 定位输入框,输入内容
        self.driver.find_element(By.ID, "query").send_keys(search_content01)
        # 加入日志,步骤信息较多且琐碎,使用debug
        logger.debug(f"搜索的信息为:{search_content01}")
        # 点击搜索
        self.driver.find_element(By.ID, "stb").click()
        # 获取搜索结果列表的标题,对应测试结果的实际结果
        search_result01 = self.driver.find_element(By.CSS_SELECTOR, "em")  # 多个em的情况下,默认拿第一个
        # 添加日志,断言使用info,级别高于debug,断言实际结果与预期结果是否一致
        logger.info(f"实际结果为:{search_result01.text},预期结果为:{search_content01}")
        # 截图记录,双重保障
        self.driver.save_screenshot("search_result01.png")
        # 断言,search_result01.text 获取搜索结果的文本信息,预期结果search_content01
        assert search_result01.text == search_content01

5、page source记录

1)使用page_source属性获取页面源码
2)在调试过程中,如果有找不到元素的错误可以保存当时的page_source调试代码
from selenium.webdriver.common.by import By
from webui.window_frame.base import Base

# 继承Base类,Base类里有前置和后置操作
class TestDataRecord(Base):
    def test_page_source_data_record(self):
        """获取page_source主要用于调试,可以不需要断言等其余信息"""
        # 现象:产生了no such element的错误
        # 方案:在报错的代码行之前打印page_source,确认定位的元素没有问题
        content = "python语言"
        # 打开网页
        self.driver.get("https://www.sogou.com/")
        # # 定位输入框,输入内容
        # self.driver.find_element(By.ID, "query").send_keys(content)
        # 获取page_source,即获取的是这个网页的源代码
        # logger.debug(self.driver.page_source)  # 可以使用debug查看输出的内容
        # 将获取的page_source写入到record.html
        with open("record.html", "w", encoding="utf-8") as f:
            f.write(self.driver.page_source)
        # 错误定位输入框
        self.driver.find_element(By.ID, "query1").send_keys(content)