自动化测试用例之元素自愈: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来管理浏览器的生命周期,使得测试更加简洁。

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

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Playwright+Pytest是一种常用的自动化测试框架组合,用于编写和执行Web应用程序的自动化测试。下面是一个简单的示例,展示了如何使用PlaywrightPytest进行自动化测试: 首先,确保已经安装了PlaywrightPytest模块。可以使用以下命令进行安装: ``` pip install playwright pytest ``` 接下来,创建一个pytest测试文件,例如 `test_example.py`,并导入所需的模块: ```python import pytest from playwright.sync_api import Playwright, sync_playwright # 使用fixture装饰器,创建一个Playwright实例 @pytest.fixture(scope="session") def playwright() -> Playwright: with sync_playwright() as playwright: yield playwright # 编写一个基本的测试用例 def test_example(playwright): # 启动一个浏览器实例 browser = playwright.chromium.launch() # 创建一个页面对象 page = browser.new_page() # 导航到目标网页 page.goto("https://www.example.com") # 断言页面标题 assert page.title() == "Example Domain" # 关闭浏览器 browser.close() ``` 在上面的示例中,使用`@pytest.fixture`装饰器创建了一个Playwright实例,作为测试用例的前置条件。然后,使用`playwright`夹具作为参数传递给测试用例函数。在测试用例中,使用Playwright的功能来启动浏览器、创建页面对象、导航到网页,并进行断言验证。 最后,使用以下命令运行测试: ``` pytest test_example.py ``` 这是一个简单的Playwright+Pytest自动化框架的示例,你可以根据实际需求扩展和定制测试用例。希望对你有帮助!如果你有任何其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blues_C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值