前言:本节主要是用Python写selenium代码,通过各种方式去定位元素,然后进行一系列的操作,可以与另外一篇 Java+Selenium做web测试 进行比对学习,看看有何区别,更喜欢使用哪种语言开发。
我是根据b站的教学视频做的笔记,网址在这,可同步观看:教学视频

selenium部分

1、通过 id 定位元素

这个实例是打开一个网站,通过id找到一个搜索的输入框,输入相应的文字,然后点击搜索按钮,等待3秒用来查看搜索出来的内容,最后关闭浏览器。

import time
from threading import Thread
from selenium import webdriver
from selenium.webdriver.common.by import By

# 创建驱动对象
driver = webdriver.Edge()
# 打开网址
driver.get("https://www.byhy.net/cdn2/files/selenium/stock1.html")
#定位搜索框,获取搜索框的值
idValue = driver.find_element(By.ID,"kw")
#输入想要搜索的内容
idValue.send_keys("科技")
#点击搜索按钮
driver.find_element(By.ID,"go").click()
# 让网页等待5秒,再关闭
time.sleep(5)
#关闭浏览器
driver.quit()
  • find_element(By.ID,“对应的id值”):通过id定位元素。(注意大小写,如果提示“没有定义by”,记得导包)
  • send_keys():在输入框中,输入相应的文本信息。
  • time.sleep(秒):让网页强制等待多少秒。

2、通过 class 定位元素

(1)这个实例是通过打开一个网站,通过class定位元素,这个网址里面的class标记了多个元素,因而用find_elements获取多个元素,并将这个列表通过循环输出查看。

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

# 创建驱动对象
driver = webdriver.Edge()
#打开示例网址
driver.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 通过class定位元素,加s返回的是多个元素
elements = driver.find_elements(By.CLASS_NAME,"plant")
#循环输出列表里面的元素
for e in elements:
    print(e.text)
  • find_elements:返回的是找到的符合条件的所有元素 , 放在一个列表中返回。

(2)这个实例也是通过class去寻找元素,唯一不同的就是,这个元素它有多个class类型,多个class类型的值之间用空格隔开的,那我们就任选其中一个属性值就行,具体代码如下:

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

# 创建驱动对象
driver = webdriver.Edge()
#打开示例网址
driver.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 通过class定位元素
element = driver.find_element(By.CLASS_NAME,"student")
#打印出来
print(element.text)

3、通过 tag名 定位元素(也叫标签名,尖括号里面的标签名字)

这个实例是用标签名字去定位一个元素的,然后将标签里面的文本打印出来,具体操作如下:

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

# 创建驱动对象
driver = webdriver.Edge()
#打开示例网址
driver.get("https://www.byhy.net/cdn2/files/selenium/stock1.html")
# 通过标签名字定位元素
element = driver.find_element(By.TAG_NAME,"nav")
#打印出来
print(element.text)

4、通过 WebElement对象 定位元素

这个实例是需要找到内层的元素,就跟扒洋葱皮一样,一层一层的去找,我们一般是用 浏览器驱动对象driver 去定位元素,我们定义的driver对象找元素是整个页面的找,webelement对象是在内部找,也就是先通过浏览器驱动去找到该元素的老大,然后这个老大再去找自己的小弟,就比较好找了。具体代码如下:

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

# 创建驱动对象
driver = webdriver.Edge()
#打开示例网址
driver.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 通过标签名字定位元素
elements = driver.find_element(By.ID,"container")
#根据找到的元素再往内层找,一层一层往里面找元素
element = elements.find_elements(By.TAG_NAME,"span")
#循环遍历输出找到的数据
for e in element:
    print(e.text)

结果:
在这里插入图片描述

5、等待元素的使用

  • 显性等待time.sleep(秒) 表示强制等待多少秒后再执行下一步
  • 隐形等待驱动器对象.implicitly_wait(秒) 该方法接受一个参数, 用来指定最大等待时长。它也是一个全局等待,只要设置过隐式等待时间,后续所有的 find_element 或者 find_elements 之类的方法都会调用该方法,在找不到元素的时候就每隔一会再找,直到超过指定时间为止。
    示例如下:
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Edge() #创建浏览器驱动
driver.implicitly_wait(10) #设置隐式等待时间为10秒
driver.get("https://www.byhy.net/cdn2/files/selenium/stock1.html")
driver.find_element(By.ID,"kw").send_keys("东风") #找到输入框输入文本
driver.find_element(By.ID,"go").click() # 找按钮点击搜索
print(driver.find_element(By.ID,"2").text) #找到id为2的数据,并打印出来
driver.quit() #退出浏览器

结果:
在这里插入图片描述

6、操控元素的方法

(1)点击元素:调用 WebElement 对象的 click 方法去点击 元素的时候, 浏览器接收到自动化命令,点击的是该元素的 中心点 位置 。因为有些按钮是组合型的按钮,包括图标和文字,有些点击整个按钮的中心位置如果不管用,就需要定位图标去点击。

(2)输入内容:

  • send_keys():输入字符串也非常简单,就是调用元素WebElement对象的send_keys方法。
  • clear():把输入框中已经有的内容清除掉,可以使用WebElement对象的clear方法。

7、获取元素信息

(1)获取元素文本内容:通过WebElement对象的 text 属性,可以获取元素 展示在界面上的 文本内容。

element = wd.find_element(By.ID, 'animal')
print(element.text)

(2)获取元素属性:通过WebElement对象的 get_attribute 方法来获取元素的属性值。就是获取class、id等这些元素的值

element = wd.find_element(By.ID, 'input_name')
print(element.get_attribute('class'))

(3)获取整个元素对应的HTML

  • 获取整个元素对应的HTML内容,可以使用element.get_attribute('outerHTML')
  • 获取某个元素 内部 的HTML文本内容,可以使用 element.get_attribute('innerHTML')

(4)获取输入框里面的文字
对于input输入框的元素,要获取里面的输入文本,用text属性是不行的,这时可以使用 element.get_attribute('value')

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
e = wd.find_element(By.ID,"searchtext")
e.send_keys("hhhhhhhh")
print(e.get_attribute("value"))

结果:
在这里插入图片描述
补充:一般用text 属性,可以获取元素 展示在界面上的 文本内容。
有时候,元素的文本内容没有展示在界面上,或者没有完全完全展示在界面上。 这时,用WebElement对象的text属性,找不到文本内容。
出现这种情况,可以尝试使用 element.get_attribute('innerText') ,或者 element.get_attribute('textContent')

8、CSS表达式选择元素

css选择器很强大,所以学会它是必然的!
(1)CSS中用class定位元素,要用.号;注意和上述的classname进行区分。

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 用css选择器,选择class为plant的元素
elements = wd.find_elements(By.CSS_SELECTOR,".plant")
# 循环输出选中的元素
for e in elements:
    print(e.text)

(2)CSS中用id定位元素,要用#号;注意和上述的id定位的区别。

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 用css选择器,选择id为searchtext的元素
element = wd.find_element(By.CSS_SELECTOR,"#searchtext")
# 定位输入框输入文字
element.send_keys("输入一些文字")
input("输入回车退出")

(3)CSS中用tag定位元素,和上述的一样,直接写标签名字就行。

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 用css选择器,选择标签名为div的所有元素
elements = wd.find_elements(By.CSS_SELECTOR,"div")
# 循环打印出来
for e in elements:
    print(e.text)

(4)关于子元素、后代元素的定位
这个知识点,我其实更喜欢用“亲子关系”和“隔代关系”去理解。

  • 亲子关系:亲自生的嘛,就离得很近,就是下一层。(中间用大于号)
    语法表达:元素1 > 元素2 > 元素3 (最终定位到元素3)

  • 隔代关系:是自己后代就行,管他隔了几层关系。(中间用空格)
    语法表达:元素1 元素2 元素3 元素4(最终定位到元素4)

  • 补充:CSS支持大于号、空格混合使用等。

示例1:(需求:获取网页中“内层11”的文本信息)

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 用css选择器,定位id为inner11,然后定位它下一层span标签
element = wd.find_element(By.CSS_SELECTOR,"#inner11 > span")
# 打印 内层11
print(element.text)

示例2:(需求:获取网页中,“中将”下的名字“孙二娘、时迁”,不要称号)

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

# 获取输入框里面的内容
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/shuihu.html")
# 用css选择器,
elements = wd.find_elements(By.CSS_SELECTOR,"#layer2 .item > .name")
# 循环打印 孙二娘、时迁
for e in elements:
    print(e.text)

结果:
在这里插入图片描述
(5)根据属性选择元素

  • CSS支持任何通过任何属性定位元素,该语法是[]
    示例1:(需求:获取页面超链接的文本)
from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1.html")
# 用css选择器,选择标签里面的属性 [href] 或者a[href] 都可以
element = wd.find_element(By.CSS_SELECTOR,"[href]")
# 打印出选中的内容
print(element.text)
  • 选择属性值包含某个字符串的元素,该语法是[属性*=任意一个属性值]

示例2:(通过class任意一个属性值找到“张三”的文本)

# 用css选择器,选择标签里面的属性
element = wd.find_element(By.CSS_SELECTOR,"[class*=chinese]")
  • 选择属性值以某个字符串开头的元素,语法是[属性^=属性值]
  • 选择属性值以某个字符串结尾的元素,语法是[属性$=属性值]
  • 一个元素有多个属性,也有办法定位到它,语法是[属性1=属性值][属性2=属性值]
    补充:可以在浏览器,打开开发者模式,ctrl+f快捷键,可以进行搜索属性。
  • 如果想选择多个元素,中间可以用,隔开。
    示例1:(需求:打印出网页“李白、静夜思、杜甫、春夜喜雨”)
from selenium import webdriver
from selenium.webdriver.common.by import By

wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1a.html")
# 用css选择器,选择标签里面的属性
elements = wd.find_elements(By.CSS_SELECTOR,"#t1 span, #t1 p")
# 打印出选中的内容
for element in elements:
    print(element.text)
  • 父元素的第n个子节点定位元素,用法是 标签名:nth-child(数字)
    nth-child()可以理解为父亲的第几个孩子,但是很多元素都可以成为父元素,那我们就需要指定标签名了,比如span:nth-child(3),意思是span类型,然后是父亲的第三个孩子。

示例: (需求:在网页中打印“李白、苏轼”)

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

wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample1b.html")
# 用css选择器,选择标签里面的属性
elements = wd.find_elements(By.CSS_SELECTOR,"span:nth-child(2)")
# 打印出选中的内容
for element in elements:
    print(element.text)
  • 父元素的倒数第n个子节点,用法是p:nth-last-child(1)选择第倒数第1个子元素,并且是p元素
  • 父元素的第几个某类型的子节点,用法是span:nth-of-type(1)第1个span类型 的子元素
  • 父元素的倒数第几个某类型的子节点,用法是p:nth-last-of-type(2)p:nth-last-of-type(2)
  • 选择的是父元素的 偶数节点,使用 nth-child(even)
  • 选择的是父元素的 奇数节点,使用 nth-child(odd)
  • 兄弟节点选择,相邻的兄弟用+号,要选择后续所有的兄弟节点用~号。

9、切换frame

当一个网页中,有frame框架,那我们就不能像往常那样写代码访问元素,需要切换到内部定位元素;不想访问了,在跳出来。有以下两行关键代码:
wd.switch_to.frame(frame的属性) 切换到frame内部,frame的定位可以通过id、class、css选择器定位,和之前一样。
wd.switch_to.default_content() 跳出frame内部。
看一个示例:(需求:进入frame内部,打印输出,再跳出来,点击一下按钮)

from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample2.html")
wd.maximize_window() #最大化窗口
wd.implicitly_wait(5) #隐式等待5秒
wd.switch_to.frame("frame1") #切换进入frame内部
es = wd.find_elements(By.CSS_SELECTOR,".plant") #打印出蔬菜
for e in es:
    print(e.text)
wd.switch_to.default_content() #切换到frame外面
wd.find_element(By.CSS_SELECTOR,"#outerbutton").click() #点击外部按钮
input("点击回车退出")

10、切换窗口

  • window_handles 返回所有窗口的列表
  • current_window_handle 返回当前窗口的单个句柄
    实例:(需求:点击网页1的链接跳转到网页2中,在搜索框中输入文字搜索后,返回网页1,点击按钮)
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/sample3.html")
wd.maximize_window() #最大化窗口
window1 = wd.current_window_handle #获取当前网页的句柄,是字符串类型
window2 = None #定义第二个窗口
wd.implicitly_wait(5) #隐式等待5秒
wd.find_element(By.TAG_NAME,"a").click() # 点击链接,打开新的窗口
time.sleep(3)
for h in wd.window_handles: #循环遍历打开的所有网页的句柄
    if h != window1: #如果找到的句柄不是网页1,那就是网页2
        window2 = h #将找到的网页2句柄赋值给win2
        break #然后跳出循环
wd.switch_to.window(window2)  #切换到新窗口网页2中
wd.find_element(By.ID,"sb_form_q").send_keys("罗小黑\n")
wd.switch_to.window(window1)#返回到第一个窗口
wd.find_element(By.ID,"outerbutton").click()#点击按钮
input("点击回车退出")

11、选择框的定位

  • 单选框radio的用法示例:(需求:打印默认选中的文本内容,点击选中小雷老师按钮)
    :check 表示当前选中的元素
from selenium import webdriver
from selenium.webdriver.common.by import By

dr = webdriver.Edge()
dr.get("https://www.byhy.net/cdn2/files/selenium/test2.html")
dr.implicitly_wait(5)
# 找到单选框的“小凯老师”打印出来
e1 = dr.find_element(By.CSS_SELECTOR,"#s_radio [value='小凯老师'][checked]")
print(e1.get_attribute("value"))
# 点击小雷老师的单选框
dr.find_element(By.CSS_SELECTOR,"#s_radio [value='小雷老师']").click()
# 定位小雷老师单选框 手动点击选中某个单选框,:checked表示当前选中的
e2 = dr.find_element(By.CSS_SELECTOR,"#s_radio [value='小雷老师']:checked")
#打印
print(e2.get_attribute("value"))
  • 复选框checkbox示例:
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
import time

webdr = webdriver.Edge()
webdr.get("https://www.byhy.net/cdn2/files/selenium/test2.html")
webdr.maximize_window()
webdr.implicitly_wait(5)
sleep(2)
# 点击小雷老师
ele1 = webdr.find_element(By.CSS_SELECTOR,"#s_checkbox  [value='小雷老师']").click()
# 点击小江老师
ele2 = webdr.find_element(By.CSS_SELECTOR,"#s_checkbox  [value='小江老师']").click()
# 循环将选中的元素,全部再点一遍,取消选中
for e in webdr.find_elements(By.CSS_SELECTOR,"#s_checkbox  [name='teachers1']:checked"):
    e.click()
sleep(3)
# 最后选中小江老师
ele3 = webdr.find_element(By.CSS_SELECTOR,"#s_checkbox  [value='小江老师']").click()
input("点击回车退出")
  • select框
    对于Select 选择框, Selenium 专门提供了一个 Select类 进行操作,可以这样导入:from selenium.webdriver.support.select import Select
    例如:select = Select(wd.find_element(By.ID, '对应的值'))
    Select类 提供了如下常用的 属性 和 方法
    1、all_selected_options 返回当前选中的options元素
    2、select_by_visible_text 根据可见的文本选择元素
    3、deselect_by_visible_text 根据可见文本取消选中
    4、deselect_all 取消选中的元素

select单选框示例:

from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/test2.html")
# 定位select单选框
select = Select(wd.find_element(By.ID,"ss_single"))
select.select_by_visible_text("小江老师") #选中小江老师
input("点击回车")

select多选框示例:

from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.select import Select

wd = webdriver.Edge()
wd.get("https://www.byhy.net/cdn2/files/selenium/test2.html")
select = Select(wd.find_element(By.ID,"ss_multi"))# 定位select多选框
sleep(2) # 等待2秒
select.deselect_all() # 取消选中的元素
select.select_by_visible_text("小江老师") # 选中小江老师
select.select_by_visible_text("小雷老师") # 选中小雷老师
sleep(3)

12、ActionChains类下面的其他方法

  • 鼠标悬停功能:driver.move_to_element
  • 拖拽元素功能:driver.drag_and_drop

具体应用看一个示例:

from time import sleep
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

driver = webdriver.Edge()
driver.get("https://www.byhy.net/cdn2/files/selenium/sample4.html")
action = ActionChains(driver)
# 鼠标悬停到元素上
action.move_to_element(
    driver.find_element(By.CSS_SELECTOR,".dropdown")
).perform()
sleep(2)
# 拖拽元素
# 循环将左边的元素全部拖拽到右边
for i in range(1,10):
    action.drag_and_drop(
        driver.find_element(By.ID,f"course-{i}"),
        driver.find_element(By.ID,"selected-courses")
    ).perform()
sleep(2)

13、冻结界面

  • setTimeout(function(){debugger},3000)在浏览器控制台输入该代码,点击回车,等待3s就会冻结界面

具体示例代码如下:

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from time import sleep

dr1 = webdriver.Edge()
dr1.get("https://www.byhy.net/cdn2/files/selenium/sample4.html#")
action = ActionChains(dr1)
# 鼠标悬停
action.move_to_element(
    dr1.find_element(By.CSS_SELECTOR,".dropdown")
).perform()
sleep(2)
#在浏览器控制台输入代码:setTimeout(function(){debugger},3000)点击回车可以冻结界面,更好的定位元素
# 定位鼠标悬停下的元素
dr1.find_element(By.CSS_SELECTOR,".dropdown-menu li:nth-child(1)").click()
sleep(2)

14、弹出框处理

  • alert 目的是显示通知消息,点击确定可关闭弹窗。
  • switch_to.alert.text 获取文本
  • switch_to.alert.accept() 点击确认
    示例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

driver = webdriver.Edge()
driver.get("https://www.byhy.net/cdn2/files/selenium/test4.html")
# alert弹窗处理
driver.find_element(By.ID,"b1").click()
sleep(2)
# 打印弹窗的文本信息
print(driver.switch_to.alert.text)
# 点击弹窗的确认按钮
driver.switch_to.alert.accept()
sleep(2)
  • confirm弹窗处理:有文本、有确认、有取消
  • switch_to.alert.dismiss() 点击取消
    示例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

driver = webdriver.Edge()
driver.get("https://www.byhy.net/cdn2/files/selenium/test4.html")
# comfirm 弹窗处理
driver.find_element(By.ID,"b2").click()
sleep(2)
print(driver.switch_to.alert.text)# 获取文本
driver.switch_to.alert.accept()# 点击确定
sleep(2)
driver.find_element(By.ID,"b2").click()#再次点击comfirm按钮
driver.switch_to.alert.dismiss()#点击取消
sleep(2)
  • prompt 弹窗处理:有输入框、确认、取消
  • switch_to.alert.send_keys("文本") 在弹窗里面输入文本
    示例:
from selenium import webdriver
from selenium.webdriver.common.by import By
from time import sleep

driver = webdriver.Edge()
driver.get("https://www.byhy.net/cdn2/files/selenium/test4.html")
# prompt 弹窗处理
driver.find_element(By.ID,"b3").click()
sleep(2)
#输入文本
driver.switch_to.alert.send_keys("selenium自动化")
# 点击确定
driver.switch_to.alert.accept()
sleep(2)
#再次点击按钮
driver.find_element(By.ID,"b3").click()
#点击取消
driver.switch_to.alert.dismiss()
sleep(2)

15、Xpath定位元素

(1)绝对定位:用/表示,/html/body/div
有点像 CSS中的 > , 表示直接 子节点关系。
(2)相对定位:用 //表示,有点像css中的空格表示隔代关系。
(3)通配符* 是一个通配符,对应任意节点名的元素,类似 CSS选择器 div > *
(4)根据属性选择: [@属性名='属性值']
注意:属性名注意前面有个@;属性值一定要用引号, 可以是单引号,也可以是双引号。
(5)根据id属性选择:选择 id 为 west 的元素,可以这样 //*[@id='west']
(6)根据class属性选择:如果一个元素class 有多个,比如:对应的 xpath 就应该是 //p[@class="capital huge-city"]不能只写一个属性
(7)属性值包含字符串

  • 要选择 style属性值 包含 color 字符串的 页面元素 ,可以这样 //*[contains(@style,'color')]
  • 要选择 style属性值 以 color 字符串 开头 的 页面元素 ,可以这样 //*[starts-with(@style,'color')]

(8)按次序选择

  • 某类型 第几个 子元素://div/p[2]要选取父元素为div 中的 p类型 第2个 子元素
  • 第几个子元素://div/*[2] 选择父元素为div的第2个子元素,不管是什么类型
  • 某类型 倒数第几个 子元素://p[last()] 选取p类型倒数第1个子元素
    //div/p[last()-2] 选择父元素为div中p类型倒数第3个子元素
  • 范围选择:
    //option[position()<=2] 选取option类型第1到2个子元素
    //*[@class='multi_choice']/*[position()>=last()-2] 选择class属性为multi_choice的后3个子元素

(9)组选择

  • xpath也有组选择, 是用 竖线 隔开多个表达式;
  • //option | //h4 要选所有的option元素 和所有的 h4 元素
    (10)选择父节点
  • 某个元素的父节点用 /.. 表示
  • 比如,要选择 id 为 china 的节点的父节点,可以这样写 //*[@id='china']/..
    (11)兄弟节点选择
  • xpath也可以选择 后续 兄弟节点,用这样的语法 following-sibling::
  • 比如,要选择 class 为 single_choice 的元素的所有后续兄弟节点 //*[@class='single_choice']/following-sibling::*
  • xpath还可以选择 前面的 兄弟节点,用这样的语法 preceding-sibling::
  • 要选择 class 为 single_choice 的元素的 所有 前面的兄弟节点 //*[@class='single_choice']/preceding-sibling::*

pytest部分

pytest 是一款主流的web自动化测试框架,比unittest更高级,插件更多,且兼容unittest

这部分的知识点,我是在b站学习的,具体网址如下:

1、标记mark

标记可以让用例与众不同,进而可以让用例区别对待

  • 用户自定义标记(只能实现用例筛选)
    步骤:
    1.注册
    先在PyCharm中创建一个独立的项目,然后在根目录新建一个文件为pytest.ini作为配置文件,在配置文件中自定义标记,在片头要表明是[pytest],用关键字markers写具体的内容。在终端控制台输入pytest --markers,就会显示我们定义好的标记。具体如下:
    在这里插入图片描述
    2.再标记:在用例上面用@符标记
    3.后筛选
    在这里插入图片描述

  • 框架内置标记(为用例增加特殊执行效果)
    1、与用户自定义标记的区别:
    1)不用注册就可以使用
    2)不仅可以筛选,还可以增加特殊效果
    3)不同的标记,增加不同的特殊效果
    skip:无条件跳过
    skipif:有条件跳过
    xfail:预期失败
    parametrize:参数化
    usefixtures:使用fixtures
    示例:
    在这里插入图片描述

2、数据驱动测试参数

语法:@pytest.mark.parametrize 是一个装饰器,用在测试函数或测试类上,基本格式如下:

@pytest.mark.parametrize("参数名字符串", 参数值列表,ids)
  • 第一个参数(argnames):必须是字符串,用来定义参数的名字。
  • 第二个参数(argvalues):必须是可迭代对象,用来提供参数的值。
  • 还有一个可选的 ids 参数,用来定制测试用例的显示名称。
    tips1:字符串没有数量限制,但必须和你每组数据里的元素个数完全一致。

举例:

# 4 个参数名
@pytest.mark.parametrize("host, port, user, db", [
    ("localhost", 3306, "root", "test_db"),
    ("192.168.1.1", 5432, "admin", "prod_db"),
    # ... 你想再加多少组数据都可以
])
def test_db_connect(host, port, user, db):
    print(f"连接 {host}:{port}, 用户={user}, 数据库={db}")

tips2:在 Python 里,列表、元组、集合、生成器等都是可迭代的。不过,最常用、也最推荐的是列表,因为列表直观、有序、好维护。
下面这些写法都是完全合法的,效果一样:

import pytest

# 1. 列表(最常用、推荐)
@pytest.mark.parametrize("user", ["admin", "guest"])
def test_with_list(user):
    print(user)

# 2. 元组(也行)
@pytest.mark.parametrize("user", ("admin", "guest"))
def test_with_tuple(user):
    print(user)

# 3. 生成器表达式(节省内存,大数据量时可用)
@pytest.mark.parametrize("num", (i for i in range(10)))
def test_with_generator(num):
    assert num < 10

tips3:ids 参数的作用,就是让你自定义每组测试数据的显示名称,让测试报告读起来更像“人话”。
举例:

data = [("admin", "123456"), ("guest", "wrong")]
ids = ["管理员正常登录", "游客密码错误"]

@pytest.mark.parametrize("user, pwd", data, ids=ids)
def test_login(user, pwd):
    pass

现在报告里的用例名就变成了:
test_login[管理员正常登录]
test_login[游客密码错误]

3、夹具fixture

作用:在用例执行之前、执行之后,自动运行代码
1、创建fixture
(1)创建函数 (2)添加装饰器 (3)添加yield关键字

@pytest.fixture
def f():
    # 前置操作
    yield
    # 后置操作

2、使用fixture
方式一:在用例参数列表中,加入fixture名字即可。
方式二:给用例加上usefixtures标记

import pytest
@pytest.fixture
def f():
    # 前置操作
    print("用例开始执行")
    yield
    # 后置操作
    print("用例执行完毕")
# 在参数列表中加函数名字   
def test_1(f):
    assert 1==1
# 在用例上加usefixtures,并标明是哪个函数名字
@pytest.mark.usefixtures("f")
def test_2():
    assert 2==1

执行结果:在控制台输入要执行的.py文件,用例开始执行前后都会执行指定的内容。
在这里插入图片描述

3、高级用法

1、自动执行:autouse=True的含义是fixture 会自动应用于它作用范围内的所有测试用例

import pytest
@pytest.fixture(autouse=True)
def f():
    # 前置操作
    print("用例开始执行")
    yield
    # 后置操作
    print("用例执行完毕")
def test_1():
    assert 1==1
def test_2():
    assert 2==1
def test_3():
    pass
def test_4():
    pass

执行结果:
在这里插入图片描述

2、依赖使用:fixture也可以依赖别的fixture,在用例参数列表添加fixture的名字

import pytest

@pytest.fixture()
def pre():
    print("fixture的依赖")

@pytest.fixture(autouse=True)
def f(pre):
    # 前置操作
    print("用例开始执行")
    yield
    # 后置操作
    print("用例执行完毕")

def test_1():
    assert 1==1

def test_2():
    assert 2==1

def test_3():
    pass

def test_4():
    pass

执行结果:会先执行被依赖的函数
在这里插入图片描述

3、返回内容:将fixture的返回值写在yield后面传给用例使用。

import pytest

@pytest.fixture(autouse=True)
def f():
    # 前置操作
    print("用例开始执行")
    yield 123
    # 后置操作
    print("用例执行完毕")

def test_1(f):
    assert 1==1
    print("得到fixture的数据:",f)

def test_2():
    assert 2==1

执行结果:
在这里插入图片描述

4、范围共享:用例与用例之间的资源共享,比如第一个用例打开浏览器,第二个用例就不用再次打开浏览器,可以等用例执行完毕再关闭浏览器。

  • 默认范围:scope=“function”
  • 全局范围:scope=“session”
    跨文件共享 conftest.py
import pytest

@pytest.fixture(autouse=True,scope="session")
def f():
    # 前置操作
    print("用例开始执行")
    yield
    # 后置操作
    print("用例执行完毕")

def test_1():
    pass

def test_2():
    pass

def test_3():
    pass

执行结果:
在这里插入图片描述
补充:范围共享是在python同一个文件中才起效的,如果要跨文件进行传递是不行的。scope="session"还要加上conftest.py可以实现。

步骤:在项目根目录中创建一个conftest.py文件,将fixture共有的内容写在这个文件内,然后分别创建多个py文件,写几个用例,执行后,发现不同文件内的用例都能共享fixture。

conftest.py文件:
在这里插入图片描述

test_f1.py文件:
在这里插入图片描述

test_f2.py文件:
在这里插入图片描述
执行结果:
在这里插入图片描述

4、插件管理

pytest插件生态是pytest特别的优势之处。
插件分成两类:

  • 不需要安装:内置插件
  • 需要安装:第三方插件

插件的启用管理:

  • 启用:-p abc
  • 禁用:-p no:abc

插件的使用方式:
1.参数
2.配置文件
3.fixture
4.mark

常用的第三方插件:

  • pytest-html
    用途:生成HTML测试报告
    安装:pip install pytest-html
    使用:--html=report.html --self-contained-html
    报告截图如下:
    在这里插入图片描述

  • pytest-xdist
    用途:分布式执行
    安装:pip install pytest-xdist
    使用:-n 次数

只有在任务本身耗时较长,超出调用成本很多的时候,才有意义

分布式执行,有并发问题:资源竞争、乱序。如果存在这种问题,就不能使用该插件。

  • pytest-rerunfailures
    用途:用例失败之后,重新执行
    安装:pip install pytest-rerunfailures
    使用:--reruns 次数 (重新再执行多少次)
    --reruns-delay 秒数 (等待多久在执行)

  • pytest-result-log
    用途:把用例的执行结果记录到日志文件中
    安装:pip install pytest-result-log
    使用:(将这些内容写在配置文件中pytest.ini)

log_file=./logs/pytest.log
log_file_level=info
1og_file_format =%(levelname)-8s %(asctime)s [%(name)s:%(lineno)s]:%(message)s
1og_file_date_format =%Y-%m-%d%H:%M:%S
#;记录用例执行结果
result_log_enable =1
I
#;记录用例分割线
result_log_separator =1
#;分割线等级
result_log_level_separator
=warning
#异常信息等级
result_log_level_verbose=info

5、企业级测试报告

allure是一个测试报告框架
安装:pip install allure-pytest
配置:--alluredir=temps --clean-alluredir(在pytest.ini写)
生成报告:allure generate -o report -c temps
打开报告:allure open report

问题:安装不了,无法正常使用allure?
解决问题:手动安装
步骤:
第一步:下载 Allure 的压缩包
正常网络下,访问 Allure 的 GitHub Releases 页面:
https://github.com/allure-framework/allure2/releases
下载最新的 allure-2.xx.x.zip 文件。
第二步:解压到固定位置
把下载的 zip 文件解压到一个你平时不会动的目录
第三步:把 Allure 的 bin 目录添加到系统 PATH
这样你才能在任意位置使用 allure 命令。
按 Win + X,选择「系统」→「高级系统设置」→「环境变量」。
在「系统变量」里找到 Path 变量,双击编辑。
点击「新建」,粘贴你的 bin 目录的完整路径
第四步:验证安装
关闭所有已打开的命令行窗口,重新打开一个新的 CMD输入:
allure --version 如果能正常显示版本号,比如 2.24.0,就代表安装成功。

allure支持对用例进行分组和关联

@allure.epic 项目
@allure.feature 功能模块
@allure.story 具体场景
@allure.title 用例标题

使用相同装饰器的用例,自动并入一组。

每个装饰器的具体作用

  1. @allure.epic("史诗名称")
  • 作用:最高级别的分类,描述一个很大的业务模块或项目里程碑。
  • 通常放在测试类上,对该类所有方法生效。
  1. @allure.feature("功能模块名称")
  • 作用:中间级别,描述 epic 下的具体功能点。
  • 通常也放在测试类上,也可以放在方法上。
  1. @allure.story("用户故事名称")
  • 作用:更细化的场景,从用户视角描述某个具体操作。
  • 通常放在测试方法上。
  1. @allure.title("用例自定义标题")
  • 作用:给测试用例起一个更人性化、更易读的名字,会覆盖报告中默认的用例函数名。
  • 放在测试方法上。

6、yaml文件格式

1、yaml完全兼容json格式,并且支持Python显示写法。

重点:
1)yaml完全兼容json
2)是数据格式,不是编程语言
3)像Python一样容易编辑和阅读
安装:pip install pyyaml

2、编写yaml文件

1.#作为注释符号
2.缩进:使用2个空格
3.成员表示:-表示列表成员
表示字典成员

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐