Python+Selenium+pytest+allure做web自动化测试
前言:本节主要是用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 用例标题
使用相同装饰器的用例,自动并入一组。
每个装饰器的具体作用:
@allure.epic("史诗名称")
- 作用:最高级别的分类,描述一个很大的业务模块或项目里程碑。
- 通常放在测试类上,对该类所有方法生效。
@allure.feature("功能模块名称")
- 作用:中间级别,描述 epic 下的具体功能点。
- 通常也放在测试类上,也可以放在方法上。
@allure.story("用户故事名称")
- 作用:更细化的场景,从用户视角描述某个具体操作。
- 通常放在测试方法上。
@allure.title("用例自定义标题")
- 作用:给测试用例起一个更人性化、更易读的名字,会覆盖报告中默认的用例函数名。
- 放在测试方法上。
6、yaml文件格式
1、yaml完全兼容json格式,并且支持Python显示写法。
重点:
1)yaml完全兼容json
2)是数据格式,不是编程语言
3)像Python一样容易编辑和阅读
安装:pip install pyyaml
2、编写yaml文件
1.#作为注释符号
2.缩进:使用2个空格
3.成员表示:-表示列表成员:表示字典成员
更多推荐


所有评论(0)