Playwright 和 Pytest 之 自动化用例自愈功能实战

2024-04-30 13:52

本文主要是介绍Playwright 和 Pytest 之 自动化用例自愈功能实战,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

当使用 Playwright 的 Locator 进行元素定位,并结合 pytest 编写自动化测试时,可以利用 pytest 的功能和 Playwright 提供的定位方法来实现自动化修复。下面我将详细展示如何实现这些功能:

自动化修复

在测试过程中捕获失败并尝试自动修复问题。我们将使用 Playwright 的 Locator 来定位元素,并在失败时重新定位更新元素。

base.py

# -*- coding: utf-8 -*-
# @Author  : blues_C
# @File    : base.py
# @Desc:import re
from utils.logger import log
from playwright.sync_api import Page, expect, Locatorclass BasePage:def __init__(self, page: Page):self.page = pagedef locator(self, selector: str) -> Locator:"""查找并返回元素"""element = self.page.locator(selector)if not element.is_visible():self.auto_repair()return elementdef click(self, selector: str):self.locator(selector).click()def fill(self, selector: str, input_value: str):self.locator(selector).fill(input_value)def auto_repair(self, scope_selector: str = "body",selector: str = "input, button, a, select, textarea, div, span, img, iframe, label, svg",filename="login_element.py"):page_title = self.page.title()scope = self.page.locator(scope_selector)elements = scope.locator(selector).all()log.info(f"自愈元素: {selector} (共 {len(elements)} 个)")# 检查文件是否存在if os.path.exists(filename):# 读取现有文件内容,以便检查变量名with open(filename, "r") as file:lines = file.readlines()existing_variables = set()# 获取现有的变量名for line in lines:if "=" in line:variable_name = line.split("=")[0].strip()existing_variables.add(variable_name)# 打开文件,准备写入新内容with open(filename, "w") as file:file.write(f"# Existing variables from {page_title}\n\n")for element in elements:element_info = f"element='{element}', "start_index = element_info.find("selector=")if start_index != -1:selector_str = element_info[start_index + len("selector='"):]end_index = selector_str.find("'")if end_index != -1:selector_content = selector_str[:end_index]# 获取各个属性text = element.inner_text()value = element.get_attribute('value')element_id = element.get_attribute('id')element_class = element.get_attribute('class')element_name = element.get_attribute('name')element_type = element.get_attribute('type')element_placeholder = element.get_attribute('placeholder')# 检查属性是否为 None,并记录非 None 的属性log_info = f"element='{selector_content}', "if text != '':log_info += f"text='{text}'  "if value != '' and value is not None:log_info += f"[value='{value}']  "if element_id is not None:log_info += f"#{element_id}  "if element_class is not None:log_info += f"[class='{element_class}']  "if element_name is not None:log_info += f"[name='{element_name}']  "if element_type is not None:log_info += f"[type='{element_type}']  "if element_placeholder is not None:log_info += f"[placeholder='{element_placeholder}']"# 打印日志信息log.info(log_info)# 如果变量名已存在,替换现有的定义行if variable_name in existing_variables:file.write(f"# {log_info} (replaced)\n")if element.get_attribute('name') is not None:variable_name = element.get_attribute('name')variable_type = selector_contentfile.write(f"{variable_name} = '{variable_type}'\n\n")existing_variables.add(variable_name)elif element.get_attribute('type') is not None:variable_name = element.get_attribute('type')variable_type = selector_contentfile.write(f"{variable_name} = '{variable_type}'\n\n")existing_variables.add(variable_name)else:variable_type = selector_contentfile.write(f"'{variable_type}'\n\n")existing_variables.add(variable_name)else:# 如果文件不存在,则创建新文件并写入内容with open(filename, "w") as file:file.write(f"# {page_title}\n")for element in elements:element_info = f"element='{element}', "start_index = element_info.find("selector=")if start_index != -1:selector_str = element_info[start_index + len("selector='"):]end_index = selector_str.find("'")if end_index != -1:selector_content = selector_str[:end_index]text = element.inner_text()value = element.get_attribute('value')element_id = element.get_attribute('id')element_class = element.get_attribute('class')element_name = element.get_attribute('name')element_type = element.get_attribute('type')element_placeholder = element.get_attribute('placeholder')info = f"element='{selector_content}', "if text != '':info += f"text='{text}'  "if value != '' and value is not None:info += f"[value='{value}']  "if element_id is not None:info += f"#{element_id}  "if element_class is not None:info += f"[class='{element_class}']  "if element_name is not None:info += f"[name='{element_name}']  "if element_type is not None:info += f"[type='{element_type}']  "if element_placeholder is not None:info += f"[placeholder='{element_placeholder}']"log.info(info)file.write(f"# {info}\n")if element.get_attribute('name') is not None:variable_name = element.get_attribute('name')variable_type = selector_contentfile.write(f"{variable_name} = '{variable_type}'\n\n")elif element.get_attribute('type') is not None:variable_name = element.get_attribute('type')variable_type = selector_contentfile.write(f"{variable_name} = '{variable_type}'\n\n")else:variable_type = selector_contentfile.write(f"'{variable_type}'\n\n")

conftest.py

import pytest
from playwright.sync_api import sync_playwright@pytest.fixture(scope='session')
def browser():with sync_playwright() as p:browser = p.chromium.launch()yield browserbrowser.close()

test_cases.py

import pytest
from common.base import BasePage
from playwright.sync_api import sync_playwrightdef test_auto_repair(browser):page = browser.new_page()page.goto('https://example.com')try:# 运行测试步骤BasePage(page).fill(login_element.username, 'username')BasePage(page).fill(login_element.password, 'password')BasePage(page).click(login_element.login)except Exception as e:pytest.fail(f'Test failed: {e}')BasePage(page).fill(login_element.username, 'username')BasePage(page).fill(login_element.password, 'password')BasePage(page).click(login_element.login)

login_element.py 示例:

# 登录到 standard
# element='form >> input,button >> nth=0', #username  [class='form-control']  [name='username']  [type='text']  
username = 'form >> input,button >> nth=0'# element='form >> input,button >> nth=1', #password  [class='form-control']  [name='password']  [type='password']  
password = 'form >> input,button >> nth=1'# element='form >> input,button >> nth=2', #id-hidden-input  [name='credentialId']  [type='hidden']  
credentialId = 'form >> input,button >> nth=2'# element='form >> input,button >> nth=3', [value='登录']  #kc-login  [class='btn btn-primary btn-block btn-lg']  [name='login']  [type='submit']  
login = 'form >> input,button >> nth=3'

总结

在上面的示例中:
pytest.fixture(scope=‘session’) 是一个 pytest fixture,用于在测试函数运行前后启动和关闭浏览器。
page.locator(‘selector’) 定位页面上的元素,如果元素不可见或定位失败,则重新定位更新元素。
如果测试步骤失败(例如,元素点击失败),则使用 pytest.fail() 来标记测试失败。

这篇关于Playwright 和 Pytest 之 自动化用例自愈功能实战的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/949041

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

【Linux 从基础到进阶】Ansible自动化运维工具使用

Ansible自动化运维工具使用 Ansible 是一款开源的自动化运维工具,采用无代理架构(agentless),基于 SSH 连接进行管理,具有简单易用、灵活强大、可扩展性高等特点。它广泛用于服务器管理、应用部署、配置管理等任务。本文将介绍 Ansible 的安装、基本使用方法及一些实际运维场景中的应用,旨在帮助运维人员快速上手并熟练运用 Ansible。 1. Ansible的核心概念

Spring框架5 - 容器的扩展功能 (ApplicationContext)

private static ApplicationContext applicationContext;static {applicationContext = new ClassPathXmlApplicationContext("bean.xml");} BeanFactory的功能扩展类ApplicationContext进行深度的分析。ApplicationConext与 BeanF

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

如何使用Ansible实现CI/CD流水线的自动化

如何使用Ansible实现CI/CD流水线的自动化 持续集成(CI)和持续交付(CD)是现代软件开发过程中的核心实践,它们帮助团队更快地交付高质量的软件。Ansible,作为一个强大的自动化工具,可以在CI/CD流水线中发挥关键作用。本文将详细介绍如何使用Ansible实现CI/CD流水线的自动化,包括设计流水线的结构、配置管理、自动化测试、部署、以及集成Ansible与CI/CD工具(如Jen