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

前言

在自动化测试领域,元素定位的准确性对于测试的成功至关重要。当使用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, Locator

class BasePage:
    def __init__(self, page: Page):
        self.page = page
        
    def locator(self, selector: str) -> Locator:
        """查找并返回元素"""
        element = self.page.locator(selector)
        if not element.is_visible():
           self.auto_repair()
        return element
        
    def 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_content
	                            file.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_content
	                            file.write(f"{variable_name} = '{variable_type}'\n\n")
	                            existing_variables.add(variable_name)
	                        else:
	                            variable_type = selector_content
	                            file.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_content
	                            file.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_content
	                            file.write(f"{variable_name} = '{variable_type}'\n\n")
	                        else:
	                            variable_type = selector_content
	                            file.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 browser
        browser.close()

测试用例编写

test_cases.py

import pytest
from common.base import BasePage
from playwright.sync_api import sync_playwright

def 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.mfbz.cn/a/586002.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

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

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

每日OJ题_DFS爆搜深搜回溯剪枝②_力扣526. 优美的排列

目录 力扣526. 优美的排列 解析代码 力扣526. 优美的排列 526. 优美的排列 难度 中等 假设有从 1 到 n 的 n 个整数。用这些整数构造一个数组 perm(下标从 1 开始),只要满足下述条件 之一 ,该数组就是一个 优美的排列 &#…

nginx缓存清理

背景 昨天打开我的gpt镜像网站,意外发现静态图片资源全都无法获取了 CoCo-AI 一番排查下来,发现是引用的cdn链接失效了 且cdn源是属于七牛云的,且不再维护,于是果断切换到cloudflare export function getEmojiUrl(unified: str…

JavaScript中的Object方法、Array方法、String方法

个人主页:学习前端的小z 个人专栏:JavaScript 精粹 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结,欢迎大家在评论区交流讨论! 文章目录 🔥Object方法🌞1 Object.is()🌞2 Object.…

区块链 | 由外部实体导致的 NFT 安全问题

🦊原文: Understanding Security Issues in the NFT Ecosystem 🦊警告: 本文只记录了原文的第 6 节。 1 问题描述 NFT 所指向的数字资产(图片、视频等)必须是可以访问的,这样 NFT 才具有意义…

iA Writer for Mac:简洁强大的写作软件

在追求高效写作的今天,iA Writer for Mac凭借其简洁而强大的功能,成为了许多作家、记者和学生的首选工具。这款专为Mac用户打造的写作软件,以其独特的设计理念和实用功能,助你轻松打造高质量的文章。 iA Writer for Mac v7.1.2中文…

数据挖掘之基于Lightgbm等多模型消融实验的信用欺诈检测实现

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 在当前的金融环境中,信用欺诈行为日益增多,给金融机构和消费者带来了巨大的损…

ThingsBoard PE专业版解决方案技术文档——温度湿度

1、项目总览 2、设备接入 3、设备告警 3.1 高温告警 创建一个Flag作为标杆,作为开启告警的开关。 3.2 低湿度告警 创建一个Flag作为标杆,作为开启告警的开关。 4、部件仪表 4.1 Entities table 部件预览: 标题样式: {"…

nuxt3项目服务端bulid后在本地浏览的3种方式(nuxi preview、Node.js Server、PM2)

你也许会问有了开发调试本地浏览,为什么还要服务端构建之后在本地浏览? 举个简单例子 在 Nuxt 3 服务端打包中,由于运行环境不同,无法直接访问 process 对象。服务端打包通常是在 Node.js 环境中进行的,而 process 对象…

Linux 手动部署JDK21 环境

1、下载包(我下载的是tar) https://www.oracle.com/cn/java/technologies/downloads/#java21 完成后进行上传 2、检查已有JDK,并删除(我原有是jdk8) rpm -qa | grep -i java | xargs -n1 rpm -e --nodeps3、清理掉 profile中的j…

vue3 安装-使用之第一篇

首先需要node版本高于V16.14.1 安装 执行 npm create vitelatest 具体选择按照自己实际需要的来 Project name:项目名称 Select a framework:选择用哪种框架 (我选择vue) Select a variant: 选择用JS还是TS(我选择JS)找到项目&…

【云原生】Docker 实践(三):使用 Dockerfile 文件构建镜像

Docker 实践(三):使用 Dockerfile 文件构建镜像 1.使用 Dockerfile 文件构建镜像2.Dockerfile 文件详解 1.使用 Dockerfile 文件构建镜像 Dockerfile 是一个文本文件,其中包含了一条条的指令,每一条指令都用于构建镜像…

笔记-PPT绘图导出高清无失真图片

问题描述:PPT绘图已经用了高清图(jpg、tif格式),但论文图片还是不清晰,打印出来还是有点糊 以下是PPT导出高清不失真图片(emf格式)的具体描述。 目录 一、绘图工具二、操作步骤 一、绘图工具 …

Java | Leetcode Java题解之第60题排列序列

题目&#xff1a; 题解&#xff1a; class Solution {public String getPermutation(int n, int k) {int[] factorial new int[n];factorial[0] 1;for (int i 1; i < n; i) {factorial[i] factorial[i - 1] * i;}--k;StringBuffer ans new StringBuffer();int[] valid…

为什么公共事业机构会偏爱 TiDB :TiDB 数据库在某省妇幼健康管理系统的应用

本文介绍了某省妇幼健康管理系统的建设和数据库架构优化的过程。原有的数据库架构使用了 StarRocks 作为分析层&#xff0c;但随着业务的发展&#xff0c;这套架构暴露出诸多痛点&#xff0c;不再适应妇幼业务的需求。为解决这些问题&#xff0c;该系统选择了将原有架构中的 St…

Cesium 3dTileset 支持 uv 和 纹理贴图

原理: 使用自定义shader实现uv自动计算 贴图效果: uv效果:

AnyMP4 Blu-ray Ripper for Mac:您的蓝光影音转换专家

AnyMP4 Blu-ray Ripper for Mac&#xff0c;一款功能强大的蓝光影音转换软件&#xff0c;让您的蓝光内容焕发新生。 AnyMP4 Blu-ray Ripper for Macv9.0.58激活版下载 它采用最高效的解决方案&#xff0c;将蓝光光盘翻录为任何您想要的视频格式&#xff0c;无论是MP4、MKV还是A…

一个单例模式中使用std::unique_ptr引起的莫名其妙的COFF损坏的问题(未解决)

使用static std::unique_ptr和static std::shared_ptr都不行struct IElementAgendaEvents {//! Called to allow listeners to modify the agenda by adding/removing entries before applying tool operation. Return true if entries added or invalidated.virtual bool …

【webrtc】MessageHandler 6: 基于线程的消息处理:StunRequest实现包发送和超时重传

G:\CDN\rtcCli\m98\src\p2p\base\stun_request.cc使用OnMessage 实现包的发送和包的超时重传StunRequest 一个StunRequest 代表是一个独立的请求的发送STUN消息 要不是发送前构造好的,要不就是按照需要构建的使用StunRequestManager: 每一个STUNRequest 携带一个交互id 写入m…

windows11安装nginx

1.解压nginx安装包到没有中文的目录 2.双击运行nginx.exe 3.任务管理器查看是否有nginx进程 4.任务管理器->性能->资源监视器 5.网络->侦听端口&#xff0c;查看nginx侦听的端口&#xff0c;这里是90端口
最新文章