【web自动化】-5- fixture集中管理和项目重构
一、投标用例设计
# 定义让前台页面保持自动登录的fixture
@pytest.fixture()
def user_driver():driver = webdriver.Chrome()driver.get("http://47.107.116.139/fangwei/")driver.maximize_window()# 创建页面类对象page = ReceptionLoginPage(driver)# 通过页面类对象调用方法执行脚本msg = page.login("admin", "msjy123")# 断言实际结果print(msg)assert msg == "成功登录"return driver
- 如何绕过登录执行投标用例流程,包括投标的正例和反例
- 在浏览器的驱动第一次执行前台页面登录之后,保持登录状态,在第二次及以上的用例执行投标
定义一个 fixture
- 完成第一次登录之后进行清除页面缓存
@pytest.fixture()
# 清除缓存的夹具,可以结合第一次登录成功之后的夹具一起结合使用
def clear_deal_page(user_driver):user_driver.get("http://47.107.116.139/fangwei/index.php?ctl=deal&id=25070")
- 在投标用例中使用清除缓存的 fixture
出现的问题:可以正常流程去登录,但是定位点击马上投标的元素超时
- 元素找不到
- 问题出现原因:登录成功之后,界面没有立马出现马上投标元素
def test_user_deal_ok(user_driver, clear_deal_page):# 投标用例执行page = ReceptionLoginPage(user_driver)msg = page.pay("msjy123")print(msg)assert msg == "投标成功!"def test_user_deal_ok2(user_driver, clear_deal_page):# 投标用例执行page = ReceptionLoginPage(user_driver)msg = page.pay("msjy123")print(msg)assert msg == "投标成功!"def test_user_deal_fail(user_driver, clear_deal_page):# 投标用例执行page = ReceptionLoginPage(user_driver)msg = page.pay("msjy1234")print(msg)assert msg == "支付密码错误"
- 由于直接进入投标界面可以实现流程用例执行
- 解决方案:
由于登录成功之后自动跳转立即投资界面
所以注释点击马上投标的元素
保持登录的夹具: user_driver 作用域改成 session 级别,保持整个页面的自动登录状态
最终结果:
- 实现投标用例在第一次登录成功之后,不需要继续进行登录,直接执行投标流程用例即可
- 作用:保持第一次之后的登录状态来完成投标流程用例执行
# 定义用例脚本执行投标步骤:
def pay(self, password):# - 点击马上投标按钮# self.find_element(*self.btn_deal_submit).click()# - 点击立即投资self.find_element(*self.btn_money).click()# - 输入支付密码self.find_element(*self.ipt_pay_password).send_keys(password)# - 点击确定按钮self.find_element(*self.btn_pay_submit).click()# 获取提示信息的实际结果断言预期结果msg = self.find_element(*self.pay_msg, need_wait=True).text# - 关闭提示信息弹框self.find_element(*self.txt_deal_msg).click()# 返回用例执行结果msgreturn msg
1. Fixture 作用域控制(scope="session"
)
如果 user_driver
Fixture 定义时指定了 scope="session"
(需补充代码确认,但从场景推断是关键),它会在整个测试会话期间只执行一次:
@pytest.fixture(scope="session") # 关键:作用域为 session,全局共享
def user_driver():driver = webdriver.Chrome()# 登录逻辑...return driver
scope="session"
意味着:整个测试运行中,user_driver
只会初始化一次浏览器、执行一次登录,所有依赖它的用例共享同一个浏览器实例,不会重复打开新窗口。
2. 保持登录状态(Cookie/Session 复用)
Web 登录本质是通过 Cookie 或 Session 维持状态:
- 首次用例执行时,
user_driver
完成登录,浏览器会缓存登录态 Cookie。 - 后续用例复用同一个
driver
实例时,浏览器自带已登录的 Cookie,访问系统时会自动识别登录状态,无需重复登录。
3. 用例依赖共享实例
测试用例通过参数注入 user_driver
,所有用例拿到的是同一个浏览器对象:
def test_bid_2(user_driver, clear_deal_page):# 复用已登录的 user_driver,无需重新打开窗口/登录page = ReceptionLoginPage(user_driver) page.pay(...)
因为浏览器实例没销毁,登录状态、页面上下文都被保留,自然 “跳过登录、不新开窗口”。
关键总结
- Fixture 作用域(
session
) 让浏览器实例全局复用,避免重复初始化。 - Cookie 持久化 让登录状态在同浏览器实例内自动延续。
- 最终实现 “一次登录、多例复用”,跳过重复登录和窗口打开流程。
在自动化测试流程里,user_driver
结合 clear_deal_page
主要是为了 解决 “登录后页面状态残留” 导致的用例干扰问题
1. 「页面缓存 / 残留状态」的干扰问题
登录后执行投标用例时,页面可能残留上一次操作的状态(比如弹窗未关闭、按钮状态异常、缓存数据未清空):
- 示例:第一次投标后,页面可能停留在 “支付成功” 提示页,或某些按钮因状态变更无法点击。
- 影响:后续用例执行时,元素定位会失败(如提示弹窗遮挡按钮),或流程因残留状态报错,导致测试不稳定。
2. clear_deal_page
的核心作用
clear_deal_page
本质是 **“重置页面环境” 的 Fixture**,通常做这些事:
@pytest.fixture()
def clear_deal_page(user_driver):# 1. 跳转到统一的“投标初始化页面”user_driver.get("http://.../deal?reset=1") # 2. 可能包含清理弹窗、重置按钮状态等操作user_driver.execute_script("window.localStorage.clear();") # 清理本地缓存return user_driver
- 重置页面上下文:强制跳转到一个 “干净” 的投标初始页,确保每次用例执行前,页面状态一致(没有残留弹窗、按钮可点击)。
- 清理缓存 / 状态:通过 JS 清理
localStorage
、sessionStorage
,或关闭残留提示框,避免上一次用例的缓存数据干扰当前用例。
3. 结合 user_driver
的必要性
user_driver
负责维持登录态(共享浏览器实例、保留 Cookie),但无法解决 “页面级的残留状态”:
- 只靠
user_driver
:登录态是保留了,但页面可能因上一次用例操作变得 “不干净”(比如弹窗未关),导致后续用例定位元素超时 / 失败。 - 结合
clear_deal_page
:在保留登录态的基础上,强制重置页面环境,让每个用例都在 “登录且页面干净” 的状态下执行,既复用了登录(提升效率),又避免了页面残留的干扰(保证稳定)。
二、fixture集中管理
fixture 的使用形式
- 用例可以直接调用
- fixture 夹具之间可以进行关联使用
- fixture 夹具可以完成任意测试用例脚本的前后置编写
- 一般情况下不同的用例使用自定义的 fixture 进行使用
为了降低用例脚本和 fixture 夹具的耦合性,将 fixture 进行集中管理
fixture 一般统一管理放置项目中固定 conftest.py 文件中
fixture 定位顺序:
- 先从当前运行的 py 文件开始定位 fixture
- 其次从同级目录的 py 文件中定位 fixture
- 再从项目包的目录中开始定位定位 fixture
- 然后从 conftest.py 文件中开始定位 fixture
- 最后会从项目所有根目录定位 fixture
- 以上查找定位 fixture 如果没有对应的夹具,那么程序会报错
三、项目重构
对框架代码进行规划和分类
在项目重构的过程中,之前导入的路径使用的模块,会随着目录结构发生变化而变化,不需要手动修改导包路径
- testcases
- 封装完成的用例脚本集中管理
- commons
- 项目公共模块集中使用包
- script
- 用例的线性脚本
- data
- 数据文件相关内容
- log
- 日志信息相关内容
- report
- 缺陷报告记录的内容
- conftest.py
- 存放 fixture 夹具的集中管理
四、统一管理项目驱动
目的:为了方便后期做兼容性测试,使用不同的浏览器来设计驱动获取
from selenium.webdriver import Chrome, Firefox, Ie, Edge, Safari
自定义函数获取对应的浏览器驱动
def get_webdriver(name: str = "chrome"):# 根据调用获取浏览器的实参来返回对应的驱动# 将具体浏览器实参名字进行整理# 将所有浏览器的名字转化为小写# 将所有浏览器的名字中空格去除# 再返回驱动的驱动name = name.lower()name = name.replace(" ", "")# print(name)# 第一种格式:# if name == "chrome":# return Chrome()# elif name == "firefox":# return Firefox()# 使用match和case关键字匹配驱动match name:case "chrome":return Chrome()case "firefox":return Firefox()case "ie":return Ie()case "edge":return Edge()
# get_webdriver("C hrome")
# get_webdriver("C H ro ME")
自定义获取驱动方式重写之后
- 需要再创建驱动的地方,调用自定义的
get_webdriver ("对应浏览器的名字")
五、后台登录的驱动
# 后台登录页面的驱动
@pytest.fixture(scope="session")
def admin_driver():driver = get_webdriver()driver.get("http://47.107.116.139/fangwei/m.php?m=Public&a=login&")# 使用cookie信息load_cookies(driver)# 如果第一次登录:没有cookie信息那么正常流程登录if is_login(driver):# 验证码处理code = save_code_img(driver)# 完成正常登录流程:使用 POM 封装调用脚本执行page = BackgroundLoginPage(driver)page.login("admin", "msjy123", code)yield driver# 第一次正常登录之后,用例执行结束之后,完成后置保持 cookie 信息save_cookies(driver)
1. 装饰器与 fixture 作用域
@pytest.fixture(scope="session")
scope="session"
:表示该 fixture 的作用域是整个测试会话(session)。也就是说,在一次pytest
测试运行过程中,这个admin_driver
夹具只会被初始化一次,然后在所有需要它的测试用例中复用,测试会话结束时才会执行其后置清理逻辑(yield
之后的代码)。这样可以提升测试效率,比如这里用于保持后台登录状态,避免重复登录操作。
2. 驱动初始化与页面访问
driver = get_webdriver()
driver.get("http://47.107.116.139/fangwei/m.php?m=Public&a=login&")
get_webdriver()
:调用了一个自定义的函数(这个函数用于根据配置获取对应的浏览器驱动实例,比如 Chrome、Firefox 等浏览器的驱动,方便进行兼容性测试 ),获取到浏览器驱动对象driver
,后续用这个驱动来操作浏览器。driver.get(...)
:使用获取到的浏览器驱动,打开指定的后台登录页面 URL,让浏览器跳转到对应的登录界面,为后续登录操作做准备。
3. Cookie 相关操作
# 使用 cookie 信息
load_cookies(driver)
load_cookies(driver)
:调用自定义函数load_cookies
,作用是尝试从某个地方加载之前保存的 Cookie 信息,并将其注入到当前的浏览器驱动driver
中。这样做的目的是,如果之前有成功登录过并保存了 Cookie,那么通过加载这些 Cookie,可以直接保持登录状态,无需再次输入账号密码等进行登录,提升测试执行效率。
4. 登录流程处理(条件判断)
# 如果第一次登录:没有 cookie 信息那么正常流程登录
if is_login(driver):# 验证码处理code = save_code_img(driver)# 完成正常登录流程:使用 POM 封装调用脚本执行page = BackgroundLoginPage(driver)page.login("admin", "msjy123", code)
is_login(driver)
:调用自定义函数is_login
,用于判断当前浏览器驱动driver
对应的页面是否处于已登录状态。可能的实现逻辑比如检查页面中是否存在登录后的特定元素(如用户名显示、登录状态标识等 ),或者根据 Cookie 信息判断会话是否有效。如果返回True
,说明需要执行正常的登录流程(一般是首次登录或者 Cookie 失效等情况 )。save_code_img(driver)
:调用自定义函数save_code_img
,用于处理登录页面的验证码。可能的操作包括截取验证码图片、识别验证码内容(如果结合了验证码识别技术,比如 OCR 等 ),并返回验证码的文本内容code
,以便后续登录时使用。BackgroundLoginPage(driver)
:这里使用了页面对象模型(POM,Page Object Model )的设计模式,创建一个BackgroundLoginPage
类的实例page
,并将浏览器驱动driver
传入。POM 模式把页面的元素定位和操作方法封装到对应的页面类中,便于代码维护和复用。page.login("admin", "msjy123", code)
:调用BackgroundLoginPage
类中封装的login
方法,传入用户名admin
、密码msjy123
和前面获取到的验证码code
,执行实际的登录操作,模拟用户在页面上输入账号、密码、验证码并提交登录的流程。
5. fixture 的 yield 关键字
yield driver
yield
是pytest
fixture 中用于分隔前置操作和后置操作的关键字。yield
之前的代码属于前置准备逻辑,执行到yield
时,会暂停当前 fixture 的执行,将driver
对象返回给调用它的测试用例使用。当所有依赖该 fixture 的测试用例执行完毕后,会回到这个 fixture,继续执行yield
之后的代码,也就是后置清理 / 保存操作。
6. 后置保存 Cookie 操作
# 第一次正常登录之后,用例执行结束之后,完成后置保持 cookie 信息
save_cookies(driver)
save_cookies(driver)
:调用自定义函数save_cookies
,在测试用例执行完毕后,将当前浏览器驱动driver
中的 Cookie 信息保存起来(比如保存到文件、数据库等 )。这样,下次再执行测试时,就可以通过前面的load_cookies
函数加载这些 Cookie,实现免登录或者保持登录状态的效果,提升后续测试的效率,也能模拟实际用户的会话保持场景。