第一章 Pytest单元测试框架基础
第二章 Pytest单元测试框架之fixture装饰器实现前后置
目录
系列文章目录
前言
一、Pytest 的setup和teardown前后置实战
二、fixture装饰器
三、conftest.py文件
前面一篇讲到用例加setup和teardown可以实现在测试用例之前或之后加入一些操作,但这种是整个脚本全局生效的,如果我想实现以下场景:
用例1需要先登录,用例2不需要登录,用例3需要先登录。很显然这就无法用setup和teardown来实现了。这就是本篇学习的目的,自定义测试用例的预置条件
Pytest提供了fixture机制,通过它可以在测试执行前后执行一些操作,类似setup和teardown。
由于本章节主要讲的是fixture装饰器,所以我们主要以类级别与函数级别进行实战:
1、类级别前后置:
# 导入所需要的包
import pytest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.by import By
class Test_login:
# 类级前置操作
def setup_class(self):
print('------>执行前置操作')
# 加载浏览器驱动
self.driver = webdriver.Chrome()
# 网页全屏
self.driver.maximize_window()
# 打开电商商城登录页
self.driver.get("https://www.KameyMall.com/login")
# 类级后置操作
def teardown_class(self):
print('------>执行后置操作')
# 用例执行完成后等待3秒
sleep(3)
# 退出浏览器
self.driver.quit()
def test_login(self):
print('用例01')
# 输入账号
self.driver.find_element(By.ID, 'fm-login-id').send_keys('*********')
sleep(2)
# 输入密码
self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('********')
sleep(2)
# 点击登录按钮
self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()
sleep(5)
def test_order(self):
print('用例02')
# 进入订单页面
self.driver.find_element(By.LINK_TEXT, 'Orders').click()
# 点击所有订单
self.driver.find_element(By.LINK_TEXT, 'My Kameymall').click()
if __name__ == '__main__':
pytest.main(['-vs'])
注意!由于我安装的是selenium4版本,所以定位的写法不同!
selenium3 :driver.find_element_by_id('元素')
selenium4 :driver.find_element(By.ID,'元素')
运行结果:
因为调用的是类级别的前后置,我的代码是有一个Test_login类,类里面有两条用例,所以执行前置操作后执行了两个用例再执行后置操作。
注意!如果用例2的元素不在用例1最后进入的页面则会执行失败,所以必须保证用例1登陆后进入的页面存在用例2的元素!
2、函数级别前后置:
# 导入所需要的包
import pytest
from selenium import webdriver
from time import sleep
from selenium.webdriver.common.by import By
class Test_login:
# 类级前置操作
def setup(self):
print('------>执行前置操作')
# 加载浏览器驱动
self.driver = webdriver.Chrome()
# 网页全屏
self.driver.maximize_window()
# 打开电商商城登录页
self.driver.get("https://www.KameyMall.com/login")
# 类级后置操作
def teardown(self):
print('------>执行后置操作')
# 用例执行完成后等待3秒
sleep(3)
# 退出浏览器
self.driver.quit()
def test_login(self):
print('用例01')
# 输入账号
self.driver.find_element(By.ID, 'fm-login-id').send_keys('********')
sleep(2)
# 输入密码
self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('********')
sleep(2)
# 点击登录按钮
self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()
def test_order(self):
print('用例02')
# 输入账号
self.driver.find_element(By.ID, 'fm-login-id').send_keys('**********')
sleep(2)
# 输入密码
self.driver.find_element(By.XPATH, '//*[@id="fm-login-password"]/div/input').send_keys('*******')
sleep(2)
# 点击登录按钮
self.driver.find_element(By.CSS_SELECTOR, 'button[class="fm-button"]').click()
sleep(5)
# 进入订单页面
self.driver.find_element(By.LINK_TEXT, 'Orders').click()
# 点击所有订单
self.driver.find_element(By.LINK_TEXT, 'My Kameymall').click()
if __name__ == '__main__':
pytest.main(['-vs'])
运行结果:
可以清晰的看到使用函数级别的前后置,每个用例都会执行一次。这里由于用例2需要先登录才能进入订单页面,所以我把登录操作也写进了用例2。接下来我们讲fixture装饰器。
1、fixture优势
firture相对于setup和teardown来说应该有以下几点优势:
@pytest.fixture函数的scope可能的取值有function,class,module,package 或 session。他们的具体含义如下:
function,表示fixture函数在测试方法执行前和执行后执行一次。
class,表示fixture函数在测试类执行前和执行后执行一次。
module,表示fixture函数在测试脚本执行前和执行后执行一次。
package,表示fixture函数在测试包(文件夹)中第一个测试用例执行前和最后一个测试用例执行后执行一次。
session,表示所有测试的最开始和测试结束后执行一次。
@pytest.fixture装饰的函数的函数名可以作为测试方法的参数,在测试方法中,使用fixture函数名作为变量,就相当于是在调用fixture装饰的函数。
fixture(scope="function", params=None, autouse=False, ids=None, name=None)
@pytest.fixture()如果不写参数,默认就是scope="function",它的作用范围是每个测试用例执行之前运行一次
如果autouse为True,则为所有测试直接激活fixture, 无需往每个函数传入fixture就可以调用它。 如果为False(默认值),则需要往测试函数传入fixture标记的函数名。
2、fixture装饰器的使用
这里主要讲fixture装饰器的scope、autouse、name。
scope :作用域(模块、类、函数。。。),不填则默认使用函数级。
autouse : False是手动调用,True是自动调用。
name : 输入名称用于用例调用(用于fixture手动调用)。
这里以类级示例:
import pytest
# 类级
@pytest.fixture(scope='class', autouse=True) # 自动调用
def web_aa():
print('------>前置操作')
yield
print('------>后置操作')
class Test_aaaa:
def test_login(self):
print('用例01')
def test_order(self):
print('用例02')
if __name__ == '__main__':
pytest.main(['-vs'])
运行结果:
可以看到运行结果与setup和teardown函数是一样的效果。
手动调用:
import pytest
# 类级
@pytest.fixture(scope='class', autouse=False, name='aaa') # 手动调用
def web_aa():
print('------>前置操作')
yield
print('------>后置操作')
# 手动调用的方式:@pytest.mark.usefixtures('aaa') 因为我们定义了name = aaa,没有定义name可以用web_aaa
@pytest.mark.usefixtures('aaa')
class Test_aaaa:
def test_login(self):
print('用例01')
def test_order(self):
print('用例02')
if __name__ == '__main__':
pytest.main(['-vs'])
运行结果:
与自动调用的结果是一样的。模块级别与函数级别是一样的调用方式。
创建conftest.py文件,讲上面的fixtrue代码放进conftest.py文件,我们现在要做的是将conftest.py文件里的前后置调用到用例文件。
# conftest.py文件
import pytest
@pytest.fixture(scope='class', autouse=False, name='aaa')
def web_aa():
print('------>执行前置操作')
yield
print('------>后置操作')
# 用例文件
import pytest
@pytest.mark.usefixtures('aaa') # 通过定义的name值调用
class Test_aaaa:
def test_login(self):
print('用例01')
def test_order(self):
print('用例02')
if __name__ == '__main__':
pytest.main(['-vs'])
执行结果: