当前位置:   article > 正文

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

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

前言

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

环境准备

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

pip install pytest
pip install playwright
  • 1
  • 2

自动化修复策略

在测试过程中捕获失败并尝试自动修复问题。我们将使用 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")
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159

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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

测试用例编写

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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

元素定位文件

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'
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

总结

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/518708
推荐阅读
  

闽ICP备14008679号