python+playwright+pytest+allure+pom+yaml实现UI自动化测试

2024-03-08 1325阅读

一. 演示项目源码

https://gitee.com/giteetangll/playwright-demo

二、UI自动化分层设计

python+playwright+pytest+allure+pom+yaml实现UI自动化测试 第1张

Auth:登录认证保存后的认证信息

BasePage:封装playwright的基础方法

BuildInLibrary:环境变量存放文件夹,可进行用例参数关联

Common:存放公共方法抽离文件夹

Config:配置文件存放文件夹

Logs:存放断言失败的记录

Pages:存放页面对象文件

TestCases:存放测试用例

TestDatas:存放测试数据

TestFiles:存放测试需要使用的文件

TestReport:存放测试报告

Utils:存放工具的封装

runner.py:项目运行文件

pytest.ini :pytest配置文件

三、po设计模型+分层设计思想

3.1BasePage层

封装常用的元素方法,后续可以自定义日志输出

# -*- coding:utf-8 -*-
"""
describe:
Author:tang
Email:tangllyx@163.com
Time: 2023/4/18
"""
from playwright.sync_api import expect, Page
from BuildInLibrary.BuildInLibrary import BuildInLibrary
class BasePage:
    def __init__(self, page: Page):
        self.page = page
    def _goto_url(self, url):
        """打开网页"""
        self.page.goto(url)
    def _click(self, locator, frame_locator=None):
        """
        点击元素
        :param locator: 传入元素定位器
        :param frame_locator: 传入frame框架的的定位器,如果没有传入,则一般点击
        :return:
        """
        try:
            if frame_locator is not None:
                self.page.frame_locator(frame_locator).locator(locator).click()
            else:
                self.page.click(locator)
        except Exception as e:
            print(e)
    def _fill(self, locator, value, frame_locator=None):
        """
        定位元素,输入内容
        :param locator:传入元素定位器
        :param value:传入输入的值
        :param frame_locator: 传入frame框架
        :return:
        """
        value = BuildInLibrary().repalce_parameter(value)
        try:
            if frame_locator is not None:
                self.page.frame_locator(selector=frame_locator).locator(selector_or_locator=locator).fill(value)
            else:
                self.page.fill(selector=locator, value=value)
        except Exception as e:
            print(e)
    def _type(self, locator, value, frame_locator=None):
        """
        模拟人工输入,一个键一个键的输入
        :param locator:传入元素定位器
        :param value:传入输入的值
        :param frame_locator: 传入frame框架
        :return:
        """
        value = BuildInLibrary().repalce_parameter(value)
        try:
            if frame_locator is not None:
                self.page.frame_locator(selector=frame_locator).locator(selector_or_locator=locator).type(text=value,delay=100)
            else:
                self.page.type(selector=locator, text=value,delay=100)
        except Exception as e:
            print(e)
    def _ele_to_be_visible(self, locator):
        """断言元素可见"""
        return expect(self.page.locator(locator)).to_be_visible()
    def _ele_is_checked(self,selector):
        """判断元素是否被选选中"""
        return self.page.is_checked(selector)
    def _browser_operation(self, reload=False, forward=False, back=False):
        """浏览器操作,reload 刷新,forward 前进,back 后退"""
        if reload:
            self.page.reload()
        if back:
            self.page.go_back()
        if forward:
            self.page.go_forward()
    def screenshot(self, path, full_page=True, locator=None):
        """截图功能,默认截取全屏,如果传入定位器表示截取元素"""
        if locator is not None:
            self.page.locator(locator).screenshot(path=path)
            return path
        self.page.screenshot(path=path, full_page=full_page)
        return path

3.2 BuildInLibrary层

设计想法是为了实现用例关联参数,设置和替换环境变量。

# -*- coding:utf-8 -*- 
"""
describe:设置全局环境变量
Author:tang
Email:tangllyx@163.com
Time: 2023/4/19 
"""
import random, time, re
class BuildInLibrary():
    glob_parameter = {}  # 存全局变量
    def set_glob_parameter(self, key, value):
        """把value的值放入变量名 key 中,"""
        # 1.提取 key   ##{{first_phone}}
        parameter_key = re.fullmatch(r'{{(\w+)}}', key).group(1)
        # 2.保存参数
        self.glob_parameter[parameter_key] = value
        return self.glob_parameter.get(parameter_key)
    def get_glob_parameter(self, key):
        self.glob_parameter['timestamp'] = str(int(time.time() * 1000))
        self.glob_parameter["timetime"] = str(int(time.time()))
        self.glob_parameter['random_phone'] = "1" + \
                                              str(random.randint(3, 9)) + \
                                              str(random.randint(0, 9)) + \
                                              time.strftime('%d%H%M%S')
        return self.glob_parameter.get(key)
    def repalce_parameter(self, text):
        """替换参数--> 可以替换多个~,满足{{$参数}}规则的会被替换"""
        parameter_key = re.findall(r'{{$(\w+)}}', text)
        for param in parameter_key:
            value = self.get_glob_parameter(param)
            to = rf"{value}"
            text = re.sub(rf'{{{{${param}}}}}', lambda m: to, text)
        return text
if __name__ == "__main__":
	pass

3.3 Common层

设计想法是为了将常用公共代码进行抽离,使测试脚本更加简洁方便

# -*- coding:utf-8 -*- 
"""
describe:allure常用方法抽离,截图方法实现
Author:tang
Email:tangllyx@163.com
Time: 2023/4/18
"""
import os.path, allure, pytest, functools
from Config.Config import Config
class PrettyAllure(object):
    @classmethod
    def PrettyAllureCase(cls, page, CaseData):
        allure.dynamic.feature(CaseData.get("模块"))
        allure.dynamic.story(CaseData.get("功能"))
        allure.dynamic.severity(CaseData.get("优先级"))
        allure.dynamic.title(f'{CaseData.get("用例编号")}_{CaseData.get("用例标题")}')
        if CaseData.get("是否执行") != "Y":
            allure.dynamic.description("用例指定跳过")
            pytest.skip("用例指定跳过")
    @classmethod
    def PrettyAllureScreenShot(cls, page, CaseData):
        filename = os.path.join(Config.test_screenshot_dir, f"{CaseData.get('用例标题')}.png")
        page.screenshot(path=filename)
        allure.attach.file(source=filename, name=CaseData.get('用例标题'), attachment_type=allure.attachment_type.PNG)
    @classmethod
    def PrettyAllureWarpper(cls, func):
        """装饰器函数"""
        @functools.wraps(func)
        def inner(*args, **kwargs):
            # 添加用例信息
            cls.PrettyAllureCase(page=kwargs.get("page"), CaseData=kwargs.get("CaseData"))  # 如何获取case_data?
            r = func(*args, **kwargs)  # 运行用例
            # 添加截图
            cls.PrettyAllureScreenShot(page=kwargs.get("page"), CaseData=kwargs.get("CaseData"))
            return r
        return inner
if __name__ == '__main__':
    pass

3.4 Config层

设计思路是为了实现配置化,方便管理,为后续持续集成铺垫。比如:URL配置,日志的配置,邮件服务配置,企业微信聊天机器人配置等

# -*- coding:utf-8 -*- 
"""
describe:配置文件
Author:tang
Email:tangllyx@163.com
Time: 2023/4/17 
Software: PyCharm
"""
import os
class Config:
    # 项目地址
    url = "http://124.223.40.245:83"
    # 项目根目录
    root_dir = os.path.split(os.path.split(__file__)[0])[0]
    test_cases_dir = root_dir + os.path.sep + "TestCases"
    test_files_dir = root_dir + os.path.sep + "TestFiles"
    test_datas_dir = root_dir + os.path.sep + "TestDatas"
    test_report_dir = root_dir + os.path.sep + "TestReport" + os.path.sep + "AllureReport"
    test_result_dir = root_dir + os.path.sep + "TestReport" + os.path.sep + "AllureResult"
    test_screenshot_dir = root_dir + os.path.sep + "TestReport" + os.path.sep + "Screenshot"
    logs = root_dir + os.path.sep + "Logs"
    # 权限认证目录
    auth_dir = root_dir + os.path.sep + "Auth"
    #浏览器
    browser = "chrome"
if __name__ == '__main__':
    print(Config.root_dir)
    print(Config.test_cases_dir)

3.5 Logs层

设计思路是存放日志文件的目录,目前只会记录断言失败的用例信息和数据

TestCases/Test_login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData1] ({'用例编号': 'Login02', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '登录成功', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234568', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': 'a[title = "退出"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData1] ({'用例编号': 'Login02', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '登录成功', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234568', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': 'a[title = "退出"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData1] ({'用例编号': 'Login02', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '登录成功', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234568', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': 'a[title = "退出"]'})
TestCases/Test_login.py::TestLogin::test_login[CaseData1] ({'用例编号': 'Login02', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '登录成功', '前置登录': {'username': 'admin', 'password': '123456'}, '账号': '13800138006', '密码': '1234568', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': 'a[title = "退出"]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Login.py::TestLogin::test_login[CaseData0] ({'用例编号': 'Login01', '模块': '用户', '功能': '登录', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '密码错误登录失败', '账号': '13800138006', '密码': '1234567', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/login.html', '断言元素定位': '//div[text()="密码错误!"]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})
TestCases/Test_Register.py::TestRegister::test_register[CaseData0] ({'用例编号': 'Register01', '模块': '用户', '功能': '注册', '优先级': 'blocker', '是否执行': 'Y', '用例标题': '注册成功', '用户名': '{{$random_phone}}', '密码': '123456', '确认密码': '123456', '验证码': '8888', 'url地址': 'http://124.223.40.245:83/index.php/Home/user/reg.html', '断言元素定位': 'a[]'})

3.6 Pages层

设计思路是为了存放页面对象,每个页面单独进行存放,页面属性和行为与用例和数据区分,便于后期维护

# -*- coding:utf-8 -*-
"""
describe:登录页面
Author:tang
Email:tangllyx@163.com
Time: 2023/4/17
Software: PyCharm
"""
import allure
from BasePage.BasePage import BasePage
class LoginPage(BasePage):
    # 元素定位器
    __username = "#username"
    __password = "#password"
    __verify_code = "#verify_code"
    __login_button = 'a[name="sbtbutton"]'
    __button_logout = 'a[]'
    @allure.step("打开登录页面")
    def goto_login(self, url):
        self._goto_url(url)
    @allure.step("输入账号")
    def fill_username(self, value):
        self._fill(self.__username, value)
    @allure.step("输入密码")
    def fill_password(self, value):
        self._fill(self.__password, value)
    @allure.step("输入验证码")
    def fill_verify_code(self, value):
        self._fill(self.__verify_code, value)
    @allure.step("点击登录按钮")
    def click_login_button(self):
        self._click(self.__login_button)
    @allure.step("点击安全退出按钮")
    def click_button_logout(self):
        self._click(self.__button_logout)
    def browser_operation(self, reload=True, forward=False, back=False):
        self._browser_operation(reload=reload, forward=forward, back=back)
# -*- coding:utf-8 -*- 
"""
describe:注册页面
Author:tang
Email:tangllyx@163.com
Time: 2023/4/20 
"""
import allure
from BasePage.BasePage import BasePage
class RegisterPage(BasePage):
    __username = "#username"
    __verify_code = 'input[placeholder="图像验证码"]'
    __password = "#password"
    __password2 = "#password2"
    __checktxt = "#checktxt"
    __btn_agree = ".regbtn.J_btn_agree"
    @allure.step("前往注册页面")
    def goto_register(self, url):
        self._goto_url(url)
    @allure.step("输入用户名")
    def type_username(self, username):
        self._type(self.__username, username)
    @allure.step("输入验证码")
    def type_verify_code(self, verify_code):
        self._type(self.__verify_code, verify_code)
    @allure.step("输入密码")
    def type_password(self, password):
        self._type(self.__password, password)
    @allure.step("输入确认密码")
    def type_password2(self, password2):
        self._type(self.__password2, password2)
    @allure.step("勾选同意")
    def click_checktxt(self):
        if not self._ele_is_checked(self.__checktxt):
            self._click(self.__checktxt)
    @allure.step("点击同意注册按钮")
    def click_btn_agree(self, ):
        self._click(self.__btn_agree)
    @allure.step("点击断言元素")
    def click_ele(self,locator):
        self._click(locator)

3.7 TestCases 层

设计思路是为了存放测试用例,使得用例和页面对象分开。维护时,只需要去维护page层即可

import allure, os
import pytest
from Pages.LoginPage.LoginPage import LoginPage
from Pages.MyAccountPage.MyAccountPage import MyAccountPage
from Utils.ReadYaml import ReadYaml
from Common.AllurePretty import PrettyAllure
from Config.Config import Config
class TestLogin:
    @pytest.mark.run(order=1)
    @PrettyAllure.PrettyAllureWarpper
    @pytest.mark.parametrize("CaseData", ReadYaml(os.path.join(Config.test_datas_dir, "TestLoginData.yaml")).read())
    def test_login(self, page, CaseData: dict):
        new_page = LoginPage(page)
        # PrettyAllure(page, CaseData).PrettyAllureCase()
        new_page.goto_login(CaseData["url地址"])
        new_page.fill_username(CaseData["账号"])
        new_page.fill_password(CaseData["密码"])
        new_page.fill_verify_code(CaseData["验证码"])
        new_page.click_login_button()
        MyAccountPage(page).logout_to_be_visible(CaseData["断言元素定位"])
# -*- coding:utf-8 -*- 
"""
describe:注册用例
Author:tang
Email:tangllyx@163.com
Time: 2023/4/20 
"""
import time
import allure, os
import pytest
from Pages.LoginPage.LoginPage import LoginPage
from Pages.RegisterPage.RegisterPage import RegisterPage
from Pages.MyAccountPage.MyAccountPage import MyAccountPage
from Utils.ReadYaml import ReadYaml
from Common.AllurePretty import PrettyAllure
from Config.Config import Config
class TestRegister:
    @pytest.mark.smoking
    @pytest.mark.run(order=0)
    @PrettyAllure.PrettyAllureWarpper
    @pytest.mark.parametrize("CaseData", ReadYaml(os.path.join(Config.test_datas_dir, "TestRegisterData.yaml")).read())
    def test_register(self, page, CaseData: dict):
        rp = RegisterPage(page)
        rp.goto_register(url=CaseData["url地址"])
        rp.type_username(username=CaseData["用户名"])
        rp.type_verify_code(CaseData["验证码"])
        rp.type_password(CaseData["密码"])
        rp.type_password2(CaseData["确认密码"])
        rp.click_checktxt()
        rp.click_btn_agree()
        MyAccountPage(page).logout_to_be_visible(CaseData["断言元素定位"])
        rp.click_ele(CaseData["断言元素定位"])

3.8 TestDatas 层

设计思路是为了存放业务数据,使数据和业务区分开来,实现数据驱动

-
  用例编号: Register01
  模块: 用户
  功能: 注册
  优先级:  blocker
  是否执行: Y
  用例标题: 注册成功
  用户名: "{{$random_phone}}"
  密码: "123456"
  确认密码: "123456"
  验证码: "8888"
  url地址: /index.php/Home/user/reg.html
  断言元素定位: a[]
-
  用例编号: Register02
  模块: 用户
  功能: 注册
  优先级:  blocker
  是否执行: Y
  用例标题: 两次输入的密码不一致,注册失败
  用户名: "{{$random_phone}}"
  密码: "123456"
  确认密码: "123456789"
  验证码: "8888"
  url地址: /index.php/Home/user/reg.html
  断言元素定位: .layui-layer-content.layui-layer-padding
-
  用例编号: Register03
  模块: 用户
  功能: 注册
  优先级:  blocker
  是否执行: Y
  用例标题: 重读手机号,注册失败
  用户名: "13811111111"
  密码: "123456"
  确认密码: "123456"
  验证码: "8888"
  url地址: /index.php/Home/user/reg.html
  断言元素定位: .layui-layer-content.layui-layer-padding

3.8 TestFiles层

设计思路是为了单独存放测试业务的所需文件

3.9 TestReport层

设计思路是为了存放测试报告、测试数据和测试据图的

python+playwright+pytest+allure+pom+yaml实现UI自动化测试 第2张

3.10 Utils 层

设计思路是为了存放常用工具的封装,比如excel文件读写,yaml文件读写,数据库操作

# -*- coding:utf-8 -*- 
"""
describe:读取yaml文件
Author:tang
Email:tallyx@163.com
Time: 2023/4/17 
Software: PyCharm
"""
import yaml, os
from Config.Config import Config
class ReadYaml(object):
    def __init__(self, filename):
        self.filename = filename
    def read(self):
        with open(file=self.filename, mode="r", encoding='utf8', ) as f:
            data = f.read()
        data_yaml = yaml.load(data, Loader=yaml.FullLoader)
        for value in data_yaml:
            value["url地址"] = Config.url + value["url地址"]
        return data_yaml
if __name__ == '__main__':
    pass
  

3.11 pytest.ini 文件

pytest配置文件

[pytest]
# 配置pytest命令行运行参数
# 空格分隔,可添加多个命令行参数 -所有参数均为插件包的参数
# addopts = -s
# 配置测试搜索的路径
# 当前目录下的TestCases文件夹 -可自定义
testpaths = ./TestCases
# 配置测试搜索的文件名
# 当前目录下的testcase文件夹下,以test_开头,以.py结尾的所有文件 -可自定义
python_files = test_*.py *_test.py
# 配置测试搜索的测试类名
# 当前目录下的testcase文件夹下,以test_开头,以.py结尾的所有文件中,以Test开头的类 -可自定义
python_classes = Test*
# 配置测试搜索的测试函数名
# 当前目录下的testcase文件夹下,以test开头,以.py结尾的所有文件中,以Test_开头的类内,以test_开头的方法 -可自定义
python_functions = test*
markers =
   smoking: 冒烟

3.12 runner.py文件

项目运行入口文件

import os
import pytest
from Config.Config import Config
if __name__ == '__main__':
   AllureReport = Config.test_report_dir
   AllureResult = Config.test_result_dir
   Screenshot = Config.test_screenshot_dir
   os.system(f"del {os.path.join(Screenshot,'*.png')}")
   pytest.main(["-v", "-s", '--reruns=3', f'--alluredir={AllureResult}', "--clean-alluredir"])
   os.system(f'allure generate {AllureResult} -o {AllureReport} --clean')

四、运行结果展示

python+playwright+pytest+allure+pom+yaml实现UI自动化测试 第3张


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]