测试分类
根据测试的时候,代码的可见度
可以分为三种情况:
- 黑盒测试(功能测试)。------------完全看不见代码
- 白盒测试(单元测试)。------------看得见所有代码
- 灰盒测试(接口测试)。------------看得见部分代码
其中,web自动化测试
属于黑盒测试。
Selenium
Selenium
是一个开源的web自动化测试工具,免费,主要做功能测试。
安装 Selenium
- 安装命令 :
pip install selenium
- 卸载命令 :
pip uninstall selenium
webdriver(对浏览器进行操作)
webdriver
是Selenium库 中的一部分,通过webdriver
可以使用代码来对浏览器进行操作。
元素定位
web自动化测试 ,首先要在网页代码(前端代码)
中找到对应的元素(例如:div,button,id等
)。 Selenium
提供8种元素定位方式。
id(标签属性) 定位
id定位
就是通过标签的id属性来定位元素 ,对于一个标签来说,id一般唯一
,前提 :元素必须要有id属性
。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_id(id) #通过元素id查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
name(标签属性) 定位
name定位
就是通过标签的name属性来定位元素 ,html 中的name 的属性值是可以重复的,即多个标签可以都叫同一个name,通过这个name来对多个标签进行统一的管理
,如添加CSS样式,前提 :元素必须要有name属性
。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_name(name) #通过元素name查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
class_name(元素的class属性) 定位
class_name定位
就是通过标签的class属性来定位元素 ,html 使用class 来定义元素的样式,一个元素的class的属性值可以有多个(这些属性值如果相同的话,会依次被覆盖)
,同时前提 :元素必须要有class属性
。
注意 :如果
class
有多个属性值,只能使用其中的一个。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_class_name(name) #通过元素class属性来查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
tag_name(标签名字) 定位
tag_name定位
就是通过标签的标签名来定位元素,每一种标签一般会在页面中存在多个,所以不方便精准定位,一般很少使用。
注意:如果存在多个相同的标签,则返回符合条件的第一个标签
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
# 如果存在多个相同的标签,则返回符合条件的第一个标签
element=driver.find_element_by_class_tag_name(tag_name) #通过标签名来查找元素的位置
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
link_text(超链接a标签) 定位
link_text定位
就是直接通过文本信息来定位超链接标签 ,文本信息必须要完全匹配对应的超链接的文本。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_link_text(link_text)
#通过文本信息来查找超链接标签的位置,文本信息必须完全匹配
element.click() #点击该超链接标签
driver.quit() #关闭浏览器
partial_link_test(超链接a标签,模糊) 定位
partial_link_test定位
与上面的link_text
的区别在于其文本信息可以只是部分匹配 ,但这个部分匹配的内容必须具有唯一性。
注意:如果没有使用具有唯一代表性的文本,则会查到多个不同结果,默认返回符合条件的第一个结果。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_partial_link_text(link_text)
#通过文本信息来查找超链接标签的位置,文本信息可以只是部分匹配
element.click() #点击该超链接标签
driver.quit() #关闭浏览器
xpath(元素路径) 定位
xpath定位
就是通过标签的路径来定位的 。所谓标签的路径 就是标签在页面上的层级,如下图: input 标签
的路径为 body--form--div--fieldset--p--input
。
注意 :
xpath
是XML Path
的简称,xml
是一种标记语言,主要应用于数据的存储与传递(常用于配置文件 ),后缀名.xml
xpath 定位策略
Xpath
虽然共有四种常用的定位策略,但是它们的语法格式却是统一的。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_xpath(xpaths)
# 其中参数xpaths根据定位策略的不同而写法不同
# 即xpaths =绝对路径或者相对路径
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
路径--定位
绝对路径:从最外层元素到指定元素之间所有经过的元素的层级的路径。
- 绝对路径是以
/html
根节点开始,使用/
来分隔元素层级,如:/html/body/div/fieldset/p[1]/input
。- 绝对路径对页面的结构要求比较严格,不建议使用。
其中/fieldset/p[2]
表示 fieldset 标签下面的第2 个p标签(下标从1开始)。
相对路径:匹配任意层级的元素,不限制元素的位置。
- 相对路径是以
//
开始。 - 格式:
//标签名
,作用是找到页面上所有的该标签。 - 格式:
//标签名/*
,作用是找到页面上所有的该标签下的所有子标签。
利用元素属性--定位
在上面相对路径的基础上,通过在路径上使用标签+属性的形式来定位,如:
格式:
//标签名[@属性名1=属性值1]
,作用是找到页面上拥有指定属性和属性值的所有的该标签。
属性与逻辑结合(多个属性)--定位
在上面相对路径的基础上,通过在路径上使用多个标签+对应的多个属性的形式来定位,如:
格式:
//标签名[@属性名1=属性值1 and @属性名2=属性值2............]
,作用是找到页面上拥有指定属性和属性值的所有的该标签。
层级与属性结合--定位
在上面相对路径的基础上,通过在路径上使用多个标签+对应的多个属性+子标签名的形式来定位,如:
格式:
//父标签名[@属性名1=属性值1 and @属性名2=属性值2............]/子标签名
,作用是找到页面上拥有指定属性和属性值的所有的该标签的对应子标签。
定位相关的提示
提示:
- 一般建议使用指定标签名称,不使用* 代替,* 的效率比较慢。
- 无论是绝对路径还是相对路径,
/
后面都必须是标签名或者*。- 在工作中,能使用相对路径,绝对不要使用绝对路径。
xpath 拓展
- 格式:
//*[text()="文本内容"]
,作用是根据标签的文本内容来查找。- 格式:
//*[contains(@属性名,"指定内容")]
,作用是查找属性对应的属性值中含有指定内容的标签。- 格式:
//*[starts-with(@属性名,"指定内容")]
,作用是查找属性对应的属性值中以指定内容开头的标签。
CSS定位
CSS选择定位
,通过选择器来定位标签 。在CSS 中,选择器是一种模式,用于选择需要添加样式的元素。在Selenium中可以使用这种选择器来定位标签。
注意:
- 在Selenium 中推荐使用CSS 定位,因为它比XPath速度要快。
- CSS选择器语法非常强大,在这里我们只学习测试中常用的几个。
CSS 定位策略
CSS选择器
虽然共有五种常用的定位策略,但是它们的语法格式却是统一的。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
id选择器
id选择器
就是根据标签的id属性来进行选择。- 格式 :
#id名
,作用查找所有id=该id名的标签。 - 例子: #UserA------选择id属性值为"UserA" 的标签。
class选择器
class选择器
就是根据标签class属性来进行选择。- 格式 :
.class
,作用查找所有class=该class属性值的标签。 - 例子: .telA------选择class属性值为"telA" 的标签。
元素选择器
- 元素选择器 就是根据标签(元素)的名字来进行选择。
- 格式 :
标签(元素)名
。 - 例子:input------选择所有的input。
属性选择器
- 属性选择器 就是根据属性名与其对应的值来进行选择。
- 格式 :
[属性名=属性值]
。 - 例子:[name="passwordA"]------选择所有的含有name属性且其对应的属性值为"passwordA"的标签。
层级选择器
- 层级选择器 就是根据层级关系和其孩子标签来进行选择。
- 格式1 :
父标签>目的标签
,这里要求该目的标签必须是父标签的儿子(直属下级)。 - 例子1:p>input------选择p标签下面的所有直属input标签。
- 格式2 :
父标签 目的标签
,这里要求该目的标签可以只是父标签的后代(不用是直属下级)。 - 例子2:p input------选择p标签下面的所有后代input标签。
CSS选择器相关提示
在CSS的各种选择器当中,这些选择器都可以互相结合使用。 如
父标签#id名>目的标签
或者父标签[属性名=属性值]>目的标签
。
css 拓展
- 格式:
[属性名^='指定内容']
,作用是查找属性名的属性值以'指定内容'开头的标签。- 格式:
[属性名$='指定内容']
,作用是查找属性名的属性值以'指定内容'结尾的标签。- 格式:
[属性名*='指定内容']
,作用是查找属性名的属性值包含'指定内容'的标签。
定位一组元素
- 格式 :
driver.find_elements_by_xxx(xxx)
;- 作用 :以列表的形式返回所有的符合条件的标签。
- 即使只有一个符合条件的标签,也要以列表形式返回。
- 返回的结果若要访问,可以通过下标或遍历来实现。
拓展------find_element方法的封装
在Selenium 库中,所有的.find_element_by_xxx(xxx)
方法的底层都是对.find_element(By.方法名1,参数1)
方法的封装。
例如 :
.find_element(By.方法名1,参数1)
就等价于.find_element_by_方法名1(参数1)
注意 :By 类的使用,需要提前引入模块 from selenium.webdriver.common.by import By
注意:在Selenium4(最新版本)中,所有的.find_element_by_xxx(xxx)
都已经被弃用,全部改为使用.find_element(By.方法名1,参数1)
了。
元素操作
当我们通过代码获取到标签(元素) 后,我们需要对标签(元素) 进行一些操作,如 点击操作,向该标签传入值,删除该标签的内容
。
- 向标签发送数据 :
.send_keys("要发送的数据");
- 上传文件 :
send_keys("文件存放路径");
- 删除标签的内容 :
.clear();
- 点击操作 :
.click()
- 在代码中,一定要先清空再输入,若不清空,则可能变成追加。
操作浏览器常用方法
浏览器对象名.maximize_window()
-----最大化浏览器窗口,即模拟浏览器窗口最大化按钮。浏览器对象名.set_window_size(width,height)
-----设置浏览器窗口大小,即设置浏览器宽高(像素点)。浏览器对象名.set_window_position(x,y)
-----设置浏览器窗口位置,即设置浏览器位置。浏览器对象名.back()
-----后退,即模拟浏览器后退按钮。浏览器对象名.forward()
-----前进,即模拟浏览器前进按钮。浏览器对象名.refresh()
-----刷新,即模拟浏览器刷新按钮。浏览器对象名.close()
-----关闭当前窗口,即模拟点击浏览器关闭按钮。浏览器对象名.quit()
-----退出浏览器,即关闭浏览器的所有窗口。浏览器对象名.title
-----获取网页的标题。浏览器对象名.current_url
-----获取当前页面的url。
获取元素信息
当我们通过代码获取到标签(元素) 后,我们需要进一步获得该标签(元素) 的一些信息,如 标签的文本内容,标签属性对应的属性值,判断该标签在页面上是否可见,判断该标签是否被选中
。
获取元素的常用方法
标签对象名.size
-----返回标签大小。标签对象名.text
-----获取该标签的文本内容。标签对象名.get_attribute("属性名")
-----获取该标签指定属性名对应的属性值。标签对象名.is_displayed()
-----判断该标签是否可见。标签对象名.is_enabled()
-----判断该标签是否可用。标签对象名.is_selected()
-----判断元素是否选中,用来检查复选框或单选按钮是否被选中。
键盘与鼠标的相关操作
鼠标操作
常见的鼠标操作有:点击,右击,双击,悬停,拖拽 等,对于这些鼠标操作,Selenium
都封装了相应的方法。封装在Selenium库中的ActionChains类 中,若要使用,需要引入头文件 from selenium.webdriver import ActionChains
。
python
from selenium.webdriver import ActionChains
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
#实例化鼠标操作对象
action=ActionChains(driver) #绑定鼠标操作对应的浏览器对象
鼠标操作常用方法
鼠标操作对象名.context_click(获取的标签对象)
-----右击,即模拟鼠标右击效果。鼠标操作对象名.double_click(获取的标签对象)
-----双击,即模拟鼠标双击效果。鼠标操作对象名.drag_and_drop(source,target)
-----拖拽,即模拟鼠标拖动效果,将source标签拖拽到target标签的位置。鼠标操作对象名.drag_and_drop(source,xoffset=360,yoffset=180)
-----拖拽,即模拟鼠标拖动效果,将source标签基于当前位置向x轴正方向360个像素点,y轴正方向180个像素点的位置进行拖拽。鼠标操作对象名.move_to_element(获取的标签对象)
-----悬停,即模拟鼠标悬停效果。鼠标操作对象名.perform()
-----执行,执行以上所有鼠标操作相关的方法。
键盘操作
键盘操作 主要是用来模拟键盘上的按键或者组合键的输入,如Ctrl+C,Ctrl+V等。selenium 中将键盘操作的方法都封装在了Keys类 当中。若要使用,需引入头文件 from selenium.webdriver.common.keys import Keys
python
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys(Keys.BACK_SPACE); #向该标签使用删除键
driver.quit() #关闭浏览器
键盘操作常用方法
标签对象名.send_keys(Keys.BACK_SPACE)
-----删除键(BACKSPACE)标签对象名.send_keys(Keys.SPACE)
-----空格键(SPACE标签对象名.send_keys(Keys.TAB)
-----制表键(Tab)标签对象名.send_keys(Keys.ESCAPE)
-----退出键(Esc)标签对象名.send_keys(Keys.ENTER)
-----回车键(Enter)标签对象名.send_keys(Keys.CONTROL,'a')
-----全选(Ctrl+A)标签对象名.send_keys(Keys.CONTROL,'C')
-----复制(Ctrl+C)标签对象名.send_keys(Keys.CONTROL,'V')
-----粘贴(Ctrl+V)标签对象名.send_keys(Keys.CONTROL,'X')
-----剪切(Ctrl+X)
元素等待
在我们使用代码定位页面标签时,可能页面还未加载出来,但我们的代码已经运行到了此处,此时就会导致代码定位失败
,这个时候就需要我们进行元素等待。
所谓元素等待,就是当页面标签未被定位到时,可以指定时间内一直等待而不报错。
为什么要设置元素等待
原因如下:
- 电脑配置较低。
- 网络速度较慢。
- 服务器处理请求慢。
元素等待类型
元素等待分为:
- 隐式等待-----其只需要设置一次,即可以对所有标签生效。
- 显式等待-----其只对某一个标签生效。
隐式等待(重要)
定位标签时,如果能定位到标签则直接返回该标签,不触发等待,如果不能定位到该元素,则间隔一段时间后再去定位元素,如果达到最大时长还未找到元素,则抛出元素不存在的异常
NoSuchElementException
。
语法 :driver.implicitly_wait(30)
,对所有标签设置隐式等待,等待时间为30秒。(一般设置为30秒) ,该语句为前置必写代码,与获取浏览器对象和浏览器窗口最大化共为3大前置必写代码。
写法:
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.implicitly_wait(10) #设置元素等待,等待时间为10秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
element=driver.find_element_by_css_selector(css_selector)
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
element.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
显式等待
定位标签时,如果能定位到标签则直接返回该标签,不触发等待,如果不能定位到该元素,则间隔一段时间后再去定位元素,如果达到最大时长还未找到元素,则抛出超时的异常
TimeoutException
在selenium
中将显式等待的相关方法封装在WebDriverWait
类中。
要想使用显式等待,需要提前导入头文件 from selenium.webdriver.support.wait import WebDriverWait
。
在使用时,需要实例化 显式等待的对象,即对显示等待相关配置进行设置 :
WebDriverWait(driver,timeout,poll_frequency=0.5)
- 参数1:driver,指浏览器对象。
- 参数2:timeout,显式等待的时间。
- 参数3:poll_frequency,时间间隔,表明每隔该间隔时间就执行一次后面的函数 ,默认为0.5秒。
实例化显式等待对象后,需要调用.until(method)
方法。.until(method) 是指直到......时候,直到method执行成功。
- method: 函数名,该函数用来实现对标签的定位。
- 一般使用匿名函数来实现:如
lambda x : x.driver.find_element_by_css_selector(css_selector)
写法:
python
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
le=WebDriverWait(driver,timeout=30,poll_frequency=0.5)
#实例化显式等待,
dy=le.until(lambda x : x.driver.find_element_by_css_selector(css_selector))
# .until() 的返回值一定是个已被定位到的标签对象
# 其中参数css_selector根据定位策略的不同而写法不同
# 即css_selector =各种CSS选择器的格式
dy.send_keys("要发送的数据"); #向该标签发送数据
driver.quit() #关闭浏览器
处理下拉选择框
在html
页面当中,下拉选择框就是<select>
标签,<select>
标签中含有option标签
,每个option标签
就代表一个选项。
-
方式一 :通过CSS选择器 直接定位到
<select>
标签中的option标签
,然后进行模拟点击操作,从而实现下拉框的选择。 -
方式二 : 使用Select类 来操作和处理
<select>
标签。
Select 类是Selenium 为操作
select
标签特殊封装的
下面具体说下方式二:
python
from selenium import webdriver
from selenium.webdriver.support.select import Select
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.maximize_window() #最大化浏览器窗口
driver.implicitly_wait(30) #设置元素等待,等待时间为30秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
select = Select(element) #实例化对象
# element 为定位到的select标签。
常用方法:
select对象名.select_by_index(下标值)
-----默认下标从0开始,根据下标值选择是第几个option标签,即是第几个选项。select对象名.select_by_value(属性值)
-----根据属性value对应的属性值 选择是第几个option标签,即是第几个选项。select对象名.select_by_visible_text(文本内容)
-----根据option标签的文本内容(选项内容) 来进行选择。
处理弹出框
在网页中,常用的弹出框有三种,分别是警告框(alert) ,确认框(confirm) ,提示框(prompt)。
当页面有弹出框时,若不处理这些弹出框,则后续的操作会全部失效。
在Selenium中对处理弹出框的操作,有专用的处理方法,并且处理的方法都一样。
- 首先,要获取弹出框对象,
alerts = driver.switch_to.alert
,所有弹出框都是使用该弹出框对象,该方法作用是定位到页面上弹出框的位置。 - 然后调用方法:
alerts.text
---返回警告框(alert) ,确认框(confirm) ,提示框(prompt) 中的文字信息。alerts.accept()
---接受对话框选项 。alerts.dismiss()
---取消对话框选项。 - 通过上面的方法可以获取页面上已经弹出的弹出框,然后进行相应的处理。
滚动条
在HTML
页面当中,由于前端技术框架的原因,页面为动态显示,元素(标签)根据滚动条下拉而被加载。
selenium
中并没有直接提供操作滚动条的方法,但它提供了可执行JavaScript
脚本的方法,所以我们可以通过JavaScript
脚本来达到操作滚动条的目的。
实现方式
1.设置JavaScript脚本 来控制滚动条: js = "window.scrollTo(0,1000)"
------------参数分别为水平滚动条与垂直滚动条分别要移动的像素点 。((0,1000)表明页面上垂直滚动条向下移动1000像素点,水平滚动条保持不动。页面上向下为y轴正方向,向右为x轴正方向)
2.selenium 调用执行JavaScript脚本 的方法: driver.execute_script(js)
。
frame切换与多窗口切换
frame 切换
frame
是HTML页面
的一个框架, 主要作用是在当前页面中的指定区域显示另一个页面的内容。
frame 标签 在HTML的主页面中的形式:
html
<iframe src="要显示的页面所在的路径" frameborder="1" height="显示区域的高度"
width="显示区域的宽度" name="myframe" id="idframe"></iframe>
由上面可见,在本页面的代码中我们是无法拿到显示区域中的另一个页面的各种标签信息的,因此我们如果想要在本页面中拿到另一个页面的标签信息,就需要采用以下方式:
- 切换到指定的frame标签,
driver.switch_to.frame(t)
----其中t可以是frame标签的id属性值或者name属性值,通过该方法,可以将浏览器driver切换到frame标签指定的页面当中,后面就可以按照正常的手段对该页面进行元素(标签)定位和元素(标签)处理了。 - 当我们在指定页面处理完成后,需要切换回主页面,
driver.switch_to.default_content()
----回到主页面。
注意 :常见的frame标签 有
<frame></frame>,<iframe></iframe>
,两者的处理方法一致(和上面一样),没有任何的不同。
多窗口切换
在HTML页面 中,当点击超链接和按钮时,有的会在新的窗口打开页面。
selenium 库中封装了获取当前窗口句柄,获取所有窗口句柄,切换到指定句柄窗口的方法。
句柄(handle):即窗口的唯一标识,浏览器可以通过句柄来区分当前窗口是哪一个。
方法:
driver.current_window_handle
-----获取当前窗口句柄。driver.window_handles
-----获取所有窗口句柄(以列表形式返回)。driver.switch_to.window(handle)
-----获取所有窗口句柄。
截屏与验证码处理
截屏
自动化脚本是由程序去执行的,因此有时候打印的错误信息并不是很明确,如果执行出错时,对当前窗口进行截图保存,那么通过图片就可以非常直观地看见出错的原因。
方法:
python
driver.get_screenshot_as_file(imgpath) #imgpath 为截图要存放的路径
验证码处理
在web应用中,大部分系统在用户登录注册的时候都要求输入验证码,而我们在设计自动化测试脚本的时候,就需要面临处理验证码的问题。
selenium中没有对验证码处理的方法,这里我们介绍下针对验证码的几种常用的处理方式:
- 去掉验证码(测试环境下采用)
- 设置万能验证码(生产环境和测试环境下采用)
- 验证码识别技术(通过python-tesseract来识别图片类型验证码,但识别率很难达到100%)
- 记录cookie(通过记录cookie 来跳过登录)(推荐此方式)
cookie(跳过登录)
cookie 是由web服务器生成的,并且保存在用户浏览器上的小文本文件,它是由键值对组成的,用来记录该用户的一些信息,通过这些信息来验证请求。
常用方法:
get_cookies()
-----获取本网站所有的本地cookie。get_cookie(name)
-----获取指定的cookie(name为cookie的名称)add_cookie(dict)
-----添加cookie(dict为一个字典对象,拥有"name","value"两个字段)
cookie中有两个字段,分别为"name"和"value",其值不同网站不同用户之间各不相同。
python
from selenium import webdriver
#获得edge浏览器对象(打开浏览器)
driver=webdriver.Edge()
driver.maximize_window() #最大化浏览器窗口
driver.implicitly_wait(30) #设置元素等待,等待时间为30秒
driver.get("网址或者html文件路径") #通过访问对应网址或者打开对应html文件
driver.add_cookie({"name":"cookie名","value":"cookie值"})
#以上cookie的name和value必须是真实有效的
#可以提前到目标网站进行登录,登录成功后,在网页后台找到cookie信息
#(不同网站方式不同,请自行上网搜索)
driver.refresh() #刷新页面,必须刷新才能看到效果
sleep(10)
#关闭浏览器
driver.quit()
PO模式
PO模式是什么
PO模式 是自动化测试的一种设计模式,P 表示Page(页面)
,O 表示Object(对象)
,它的主要作用是提高代码可维护性,复用性,可读性。
PO模式的核心思想: 将每个页面(或组件)抽象为一个类 ,页面中的元素定位 和操作逻辑 封装在这个类中,测试用例通过调用这些类的方法来完成交互 ,而不是直接在测试脚本中操作元素。这样,当页面元素或结构变化时,只需修改对应的页面类,而无需修改测试用例
。
注意 :在类中,元素定位与操作元素要分开封装 ,对于元素操作而言,每一个操作都要封装为一个方法,例如:输入用户名,输入密码,这两个操作就要分开封装。
代码编写规范
页面层(page):
- 类名使用大驼峰格式,有下划线的去掉下划线,如PageLogin。
- 根据业务需求每个操作步骤单独封装为一个方法,方法名要以page_开头。
- 根据业务需求,组装这些基础方法,方法名同样要以page_开头。
业务层(scripts):
- 该层是用来编写测试用例的,通过调用页面层的类的方法来完成交互。
基类层(base):
- 为了解决页面层的代码冗余性问题(重复代码太多了),因此就出现了基类层。
- 将页面层当中的公共方法进行统一的封装,封装为一个模块,让该模块能够被任何所有
web自动化
项目调用。 - 具体做法 :在基类层中将公共方法封装成一个类,在业务层中页面类继承基类层中的类。
- 基类层类名Base ,方法名要以base_开头。
- 常用的公共方法有:
查找元素(标签)方法,点击元素(标签)方法,输入方法,获取文本内容方法,截图方法
。
python
tt=(123,"ksd") #元组
print(tt) #解包前
# 结果: ('123','ksd')
#解包元组
print(*tt) #解包后
# 结果: 123,ksd
基类层(Base)代码实现:
python
class Base
def __init__(self,driver): #初始化
self.driver = driver
#查找元素的方法(提供点击,输入,获取文本使用)
def base_find_element(self,loc,timeout=30,poll_frequency=0.5):
return WebDriverWait(self.driver,timeout=timeout,poll_frequency=poll_frequency)
.until(lambda x : x.find_element(*loc))
#显式等待,loc是一个元组, *loc表明解包元组,即获取到该元组的数据
#点击方法
def base_click(self,loc):
self.base_find_element(loc).click()
#输入方法
def base_input(self,loc,value):
el = base_find_element(loc) #查找元素
el.clear() #清空输入框
el.send_keys(value) #向标签(元素)传入数据
#获取文本方法
def base_get_text(self,loc):
return self.base_find_element(loc).text
#截图方法
def base_get_image(self):
self.driver.get_screenshot_as_file("截图文件存放位置")
页面层(Page)代码实现:
python
from selenium.webdriver.common.by import By
'''临时数据存放地'''
#登录链接
login_link = (By.PARTIAL_LINK_TEXT, "登录")
#用户名
login_username = (By.ID, "username")
#密码
login_pwd = (By.ID, "password")
#验证码
login_verify_code = (By.ID, "verify_code")
#登录按钮
login_btn = (By.CSS_SELECTOR, ".J-login-submit")
#获取异常文本信息
login_err_info = (By.CSS_SELECTOR, ".layui-layer-content")
#点击异常提示框 按钮
login_err_btn_ok = (By.CSS_SELECTOR, ".layui-layer-btn0")
#以一个登录页面为例:
class PageLogin(Base):
def __init__() #初始化页面对象
super().__init__()
#点击登录链接
def page_click_login_link(self):
self.base_click(login_link)
#输入用户名
def page_input_username(self,username):
base_input(login_username,username)
#输入密码
def page_input_password(self,password):
base_input(login_pwd,password)
#输入验证
def page_input_verify_code(self,code):
base_input(login_verify_code,code)
#点击登录按钮
def page_click_login_btn(self):
base_click(login_btn)
#获取异常提示信息
def page_get_error_info(self):
return base_get_text(login_err_info)
#点击异常信息框 确定
def page_click_err_btn_ok(self):
base_click(login_err_btn_ok)
#截图
def page_get_screenshot(self):
base_get_image()
#登录的组合业务
def page_login(self,username,password,code):
self.page_input_username(username) #输入用户名
self.page_input_password(password) #输入密码
self.page_input_verify_code(code) #输入验证信息
self.page_click_login_btn() #点击登录按钮
业务层(scripts)代码实现:
python
import unittest
from **** import PageLogin
from parameterized import parameterized
import GetDriver
def get_data() #获取到测试用例的数据
return [("用户名1","密码1","验证码1","预期结果1"),
("用户名2","密码2","验证码2,预期结果2")]
class TestLogin(unittest.TestCase):
@classmethod
def setUpClass(cls,url):
cls.driver = GetDriver().get_driver(url)
cls.login = PageLogin(driver) #获取页面对象
cls.login = page_click_login_link() #点击登录链接
@parameterized.expand(get_data()) #参数化
def test_login(self,username,password,code,expect):
#登录
cls.login.page_get_screenshot(self,username,password,code)
#获取登录提示信息
msg = self.login.page_get_error_info()
try:
#断言
self.assertEqual(expect,msg)
except AssertionError:
#截图
cls.login.page_get_screenshot()
@classmethod
def tearDownClass(cls): #关闭浏览器,这个driver来自于基类层(base)
GetDriver().quit_driver()
在基类层中单独封装一个
driver类
,用于处理浏览器的相关内容。
driver类的代码实现:
python
from selenium import webdriver
class GetDriver
#设置类属性
driver = None
@classmethod
def get_driver(cls,url): #获取driver
if cls.driver is None:
cls.driver = webdriver.Edge() #获得edge浏览器对象(打开浏览器)
cls.driver.maximize_window() # 最大化浏览器窗口
cls.driver.implicitly_wait(30) # 设置元素等待,等待时间为30秒
#url="http://localhost:8080"
cls.driver.get(url) #访问网址
return cls.driver
@classmethod
def quit_driver(cls): #关闭driver
if cls.driver:
cls.driver.quit()
cls.driver = None
数据驱动
数据驱动:是以数据来驱动整个测试用例的执行,也就是测试数据决定测试用例的结果。
数据驱动的特点:
- 数据驱动本身不是一个工业级标准概念,因此在不同公司都会有不同的解释。
- 可以将数据驱动理解成一种模式或者一种思想。
- 数据驱动技术可以将用户的关注点放在对测试数据的构建和维护上,而不是在自动化脚本代码上,可以利用同样的过程对不同的数据输入进行测试。
- 数据驱动的实现要依赖参数化的技术。
JSON格式转换
日志
日志 就是用于记录系统运行时的信息 ,对一个事件的记录也被称为Log
。
日志的作用
- 调试程序。
- 了解系统运行的情况,是否正常。
- 系统程序运行故障分析与问题定位。
- 用来做用户行为分析和数据统计。
日志的级别
所谓日志的级别,即日志内容的优先级。
日志的基本用法
python
中有个标准库logging ,其可以直接记录日志信息。 使用前要引入头文件 :import logging.handlers
。
常用方法:
logging.basicConfig(level=logging.DEBUG)
-----设置日志级别(这里是设置为调试级别)。logging.debug("调试信息")
-----打印(输出)调试信息。logging.info("普通信息")
-----打印(输出)普通信息。logging.warning("警告信息")
-----打印(输出)警告信息。logging.error("错误信息")
-----打印(输出)错误信息。logging.critical("严重错误信息")
-----打印(输出)严重错误信息。
当为程序指定一个日志级别后,程序会记录所有日志级别大于或等于指定日志级别的日志信息。
设置日志输出格式
python
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
#日志输出格式: 当前时间 日志级别 [logger的名字] [日志输出函数所在模块名 日志输出函数名:日志输出语句所在代码行] - 用户输出的消息
logging.basicConfig(level=logging.DEBUG,format=fmt)
#设置日志级别,并修改日志输出格式
logging.basicConfig(level=logging.DEBUG,format=fmt,filename="文件保存路径")
,通过此方法可以将日志输出到指定文件内保存----自动在文件末尾追加输出。
日志的高级用法(重点)
logging日志
模块的四大组成部分:
日志的各个部分的关系:
日志器(logger)
需要通过处理器(handler)
将日志信息输出到目标位置,如文件,网络等。- 不同
处理器(handler)
可以将日志输出到不同位置。日志器(logger)
可以设置多个处理器(handler)
将同一条日志记录输出到不同位置。- 每个
处理器(logger)
可以设置自己的格式器(formatter)
实现同一条日志以不同格式输出到不同地方。- 每个
处理器(logger)
都可以设置自己的过滤器(filter)
实现日志过滤,从而保留感兴趣的日志。
简单点说就是:日志器(logger)
是入口,真正干活的是处理器(handler)
,处理器(handler)
还可以通过过滤器(filter)
和格式器(formatter)
对要输出的日志内容做过滤和格式化等处理操作。
日志器与处理器
日志器
获取日志器 (logger
):
python
logger = logger.getLogger(日志器名字)
# logger = logger.getLogger() 此时日志器为默认名字root
打印日志器 (logger
):
python
logging.debug("调试信息") #打印(输出)调试信息
logging.info("普通信息") #打印(输出)普通信息
logging.warning("警告信息") #打印(输出)警告信息
logging.error("错误信息") #打印(输出)错误信息
logging.critical("严重错误信息") #打印(输出)严重错误信息
其他日志器相关方法:
python
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
logger.addHandler() #为该logger对象添加一个handler对象
logger.addFilter() #为该logger对象添加一个filter对象
处理器
程序中不应该直接实例化和使用
Handle实例
,应该使用Hander
实现类,即Hander
的子类来创建对象。
其中,Hander
的子类有:
logging.StreamHandler()
-----将日志输出到控制台。logging.FileHandler()
-----将日志输出到文件(默认文件大小会无限增长)。logging.handlers.RotatingFileHandler()
-----将日志输出到文件(可以将文件按指定大小自动划分并保存)。logging.handlers.TimedRotatingFileHandler()
-----将日志输出到文件(可以将文件按保存时间自动划分 )。(常用)logging.handlers.HTTPHandler()
-----将日志以GET
或者POST
的方式发送给一个HTTP服务器。logging.handlers.SMTPHandler()
-----将日志发送给一个指定的email地址
。
使用方法:
python
import logging.handlers
logger = logger.getLogger() #获取日志器
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
sh=logging.StreamHandler() #设置处理器,将日志输出到控制台
logger.addHandler(sh) #为该logger对象添加一个handler对象
logging.warning("警告信息1") #打印(输出)警告信息
logging.warning("警告信息2") #打印(输出)警告信息
#以上代码结果:警告信息1和警告信息2被依次输出到控制台
常用:
logging.handlers.TimedRotatingFileHandler(filename,when)
-----将日志输出到文件(可以将文件按保存时间自动划分)。 其中filename表明日志文件(.log)将要存放的路径位置,when表示划分一次的时间单位。其中when的时间单位有S------Seconds,M------Minutes,H------Hours,D------Day,midnight------一整个日夜,W{0~6}------week, 0:Monday(0表示星期一)
python
import logging.handlers
logger = logger.getLogger() #获取日志器
logger.setLevel(logging.INFO) #设置日志器处理日志的最低级别
sh=logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",when="M",interval=3,backupCount=3) #设置处理器,将日志输出到控制台
#interval 保存文件的时间间隔的数值
#backupCount 要保存文件的总个数
logger.addHandler(sh) #为该logger
对象添加一个handler对象
logging.warning("警告信息1") #打印(输出)警告信息
logging.warning("警告信息2") #打印(输出)警告信息
格式器
处理器可以添加多个格式器(logger.Formatter())。
python
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
fm = logger.Formatter(fmt) #设置格式器
sh=logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",when="M",interval=3,backupCount=3) #设置处理器,将日志输出到控制台
#interval 保存文件的时间间隔的数值
#backupCount 要保存文件的数量,当保存文件个数超过时,会自动删除就文件,以保证当前的文件数量小于或等于backupCount
sh.setFormatter(fm) #将格式器添加到对应的处理器中
sh.setLevel(logging.ERROR)
#设置处理器的日志级别,该处理器只会输出ERROR级别的日志信息
封装logger
python
import logging.handlers #导包
class GetLogger:
logger = None
@classmethod
def get_logger(cls):
if cls.logger is None:
#获取日志器
cls.logger = logging.getLogger()
#设置日志器级别
cls.logger.setLevel(logging.INFO)
#获取处理器(输出到控制台)
sh = logging.StreamHandler()
#获取处理器(输出到文件,文件按保存时间自动划分)
th = logging.handlers.TimedRotatingFileHandler(filename="文件存储路径",
when="midnight",interval=1,backupCount=30,encoding = "uft-8")
# 设置格式器
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s(%(funcName)s:%(lineno)d)] - %(message)s"
fm = logger.Formatter(fmt)
#将格式器添加到处理器(输出到控制台)
sh.setFormatter(fm)
#将格式器添加到处理器(输出到文件,文件按保存时间自动划分)
th.setFormatter(fm)
#将处理器添加到日志器当中
cls.logger.addHandler(sh)
cls.logger.addHandler(th)
return cls.logger