自动化测试用例之元素自愈:Playwright与pytest的结合使用

news/2024/5/22 5:56:15

前言

在自动化测试领域,元素定位的准确性对于测试的成功至关重要。当使用Playwright结合pytest进行测试时,我们可以通过一些策略来增强测试的鲁棒性,特别是在元素定位失败时能够自动进行修复。本文将详细介绍如何实现这一过程。

环境准备

首先,确保你的环境中安装了playwrightpytest。可以通过以下命令安装:

pip install pytest
pip install playwright

自动化修复策略

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

BasePage类设计

BasePage类封装了页面操作和自动修复的逻辑。

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")

pytest fixture的使用

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'

总结

本文展示了如何使用Playwright的Locator结合pytest的自动化测试框架来实现元素的自动定位和修复。通过封装页面操作和自动修复逻辑到BasePage类中,我们可以提高测试的稳定性和可维护性。同时,使用pytest的fixture来管理浏览器的生命周期,使得测试更加简洁。

通过上述方法,我们能够确保即使在面对复杂的页面元素变化时,我们的自动化测试也能够适应并成功执行,从而提高测试的覆盖率和准确性。


http://www.mrgr.cn/p/33750118

相关文章

【缓存服务】⭐️自定义实现一个简易的数据缓存

目录 🍸前言 🍻手写缓存服务 (1)缓存实体类 (2)缓存工具类 (3)测试缓存服务 🍷已有的缓存工具 🍹章末 🍸前言 俗话说 有轮子不用 就是玩 开个…

数字电路-5路呼叫显示和8路抢答器

本内容涉及两个电路,分别为5路呼叫显示电路和8路抢答器电路,包含Multisim仿真原文件,为掌握FPGA做个铺垫。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易! 目录: 一、5路呼叫显…

《Git---Windows Powershell提交信息中文乱码解决方案》

解释: Windows PowerShell中的Git乱码通常是因为字符编码不正确或Git配置不支持Windows系统的默认编码导致的。Git在处理文件时可能使用UTF-8编码,而Windows系统的命令行工具(如PowerShell)默认使用的是Windows-1252或GBK编码。 …

【代码问题】【Pytorch】训练模型时Loss为NaN或INF

解决方法或者问题排查: 加归一化层: 我的问题是我新增的一个模块与原来的模块得到的张量相加,原张量是归一化后的,我的没有: class Module(nn.Module):def __init__(self,dim,):super().__init__()# 新增一个LayerNo…

http1.1和http2.0的同源请求数限制

判断协议版本 :scheme: 在请求头中表示使用的是HTTP/2协议。即 出现 :开头的请求头Chrome 只支持查看 HTTP/1.x 的 Raw Headers,对这种请求,会给出 view source 选项。HTTP2.0不给出。可继续学习 https://www.cnblogs.com/kirito-c/p/10360868.html抓包…

代码随想录算法训练营DAY32|C++贪心算法Part.2|122.买卖股票的最佳时机II、55.跳跃游戏、45.跳跃游戏II

文章目录 122.买卖股票的最佳时机II思路CPP代码 55.跳跃游戏思路CPP代码 45.跳跃游戏II思路方法一代码改善 CPP代码 122.买卖股票的最佳时机II 力扣题目链接 文章讲解:122.买卖股票的最佳时机II 视频讲解: 状态:本题可以用动态规划&#xff0…

mac配置maven

在 macOS 上配置 Maven 也相对简单。以下是一种常用的方法: 1. 安装maven **下载 Maven:**首先,你需要从 Maven 官网(https://maven.apache.org/download.cgi)下载最新版本的 Maven。你可以选择二进制压缩包&#xf…

接口测试和Mock学习路线(中)

1.什么是 swagger Swagger 是一个用于生成、描述和调用 RESTful 接口的 WEB 服务。 通俗的来讲,Swagger 就是将项目中所有想要暴露的接口展现在页面上,并且可以进行接口调用和测试的服务。 现在大部分的项目都使用了 swagger,因为这样后端…

ArgoCD集成部署到Kubernetes

1:环境 kubernetes1.23.3ArgoCD2.3.3 2:ArgoCD介绍 Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. Argo CD是一个基于Kubernetes的声明式的GitOps工具。 那么,什么是GitOps呢? GitOps是以Git为基…

arcgis js 4.x加载SceneLayer并实现基于属性查询定位及高亮

一、代码 <!DOCTYPE html> <html> <head><meta charset"utf-8" /><meta name"viewport" content"widthdevice-width, initial-scale1,maximum-scale1,user-scalableno"><title></title><link rel…

​HTTP与HTTPS:网络通信的安全卫士

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

美国洛杉矶站群服务器如何提高网站排名?

美国洛杉矶站群服务器怎么样?美国洛杉矶站群服务器如何提高网站排名?Rak部落小编为您整理发布美国洛杉矶站群服务器如何提高网站排名? 美国洛杉矶站群服务器可以通过以下几种方式帮助提高网站排名&#xff1a; - **提升网站性能**&#xff1a;美国站群服务器通常配备高速CPU…

实操——使用uploadify插件(php版和Java版) 与 Dropzone.js插件分别实现附件上传

实操——使用uploadify插件&#xff08;php版和Java版&#xff09;与 Dropzone.js插件分别实现附件上传 1. 使用uploadify插件上传1.1 简介1.1.1 简介1.1.2 参考GitHub 1.2 后端PHP版本的uploadify1.2.1 下载项目的目录结构1.2.2 测试看界面效果1.2.3 附页面代码 和 PHP代码 1.…

由于找不到msvcr80.dll,无法继续执行代码的解决方法

在日常使用电脑进行工作或娱乐时&#xff0c;您可能会遇到一个令人困惑的情况&#xff1a;屏幕上突然弹出一个错误提示&#xff0c;明确指出“msvcr80.dll文件丢失”&#xff0c;这个错误通常会导致某些应用程序无法正常运行。那么&#xff0c;当我们遇到这个问题时&#xff0c…

JavaScript基础(二)

JavaScript基础&#xff08;一&#xff09; 相信看完上一篇文章的你对变量的类型和使用已经了解了&#xff0c;接下来我们将对变量间的转化以及Js中的三大结构展开叙述。 类型转换 首先&#xff0c;我们要了解为什么我们需要转换类型呢&#xff1f; 在表单提交中&#xff0c;…

mysql基础知识汇总

本文自行整理&#xff0c;只做学习记忆之用&#xff0c;若有不当之处请指出 一、数据库三层结构 &#xff08;1&#xff09;所谓安装Mysql数据库&#xff0c;就是在主机安装一个数据库管理系统(DBMS),这个管理程序可以管理多个数据库。DBMS(database manage system) &#xf…

Docker私有仓库搭建

下载离线镜像 检查Docker环境 docker versionDocker Hub 中registry 最新版本为 2.8.3&#xff0c;详见 registry . https://hub.docker.com/_/registry/tags 下载镜像 docker pull registry:2.8.3离线导出&#xff0c;方便在无法联网的设备上安装 docker image save regi…

图像在神经网络中的预处理与后处理的原理和作用(最详细版本)

1. 问题引出及内容介绍 相信大家在学习与图像任务相关的神经网络时&#xff0c;经常会见到这样一个预处理方式。 self.to_tensor_norm transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 具体原理及作用稍后解释&…

华夏芯产品技术概述

华夏芯产品技术概述GPTX1 CPU 概述: GPTX1 CPU是华夏芯完全自主知识产权、自主架构的面向嵌入式的高能效CPU核。此CPU核依托Unity指令集,针对先进半导体工艺对微架构和流水线进行了深度优化,能够在相同工艺下达到更高的主频和更高的能效,应用于网络、通讯、数字电视、存储等…

FANUC机器人SOCKET连接指令编写

一、创建一个.KL文件编写连接指令 创建一个KL文本来编写FANUC机器人socket连接指令 二、KAREL指令代码 fanuc机器人karel编辑器编辑的karel代码如下&#xff1a; PROGRAM SM_CON %COMMENT SOCKET连接 %STACKSIZE 4000 --堆栈大小 %INCLUDE klevccdfVAR status,data_type,in…