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

这篇具有很好参考价值的文章主要介绍了python+playwright+pytest+allure+pom+yaml实现UI自动化测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一. 演示项目源码

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

二、UI自动化分层设计

python+playwright+pytest+allure+pom+yaml实现UI自动化测试
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[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[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[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[title="退出"]'})
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[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[title="退出"]'})
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[title="退出"]'})
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[title="退出"]'})
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[title="退出"]'})
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[title="退出"]'})
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[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[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[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[title="退出"]'})
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[title="退出"]'})
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[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[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[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[title="退出"]'})

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[title="退出"]'

    @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[title="退出"]

-
  用例编号: 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自动化测试

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自动化测试文章来源地址https://www.toymoban.com/news/detail-500063.html

到了这里,关于python+playwright+pytest+allure+pom+yaml实现UI自动化测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • Yaml版接口自动化详细讲解(Python + pytest + allure + log + yaml + mysql + redis + 钉钉通知 + Jenkins)

    目录 框架介绍 框架结构 框架功能讲解 common  # 配置         config.yaml  # 公共配置         setting.py  # 环境路径存放区域 data  # 测试用例数据 Files  # 上传文件接口所需的文件存放区域 logs  # 日志层 report  # 测试报告层 test_case  # 测试用例代码         __init

    2024年02月13日
    浏览(20)
  • 基于Python+Pytest+Playwright+BDD的UI自动化测试框架

    本框架是基于Python+Pytest+Playwright+BDD的UI自动化测试框架。 测试社区地址: https://www.gitlink.org.cn/zone/tester 入群二维码:https://www.gitlink.org.cn/floraachy/apiautotest/issues/1 对于框架任何问题,欢迎联系我! 支持通过命令行指定浏览器,选择需要运行的浏览器。 支持通过命令行指定运行

    2024年02月07日
    浏览(16)
  • 【UI自动化测试技术】自动化测试研究:Python+Selenium+Pytest+Allure,详解UI自动化测试,学习模拟鼠标+模拟键盘进行相关操作(精)(四)

    1、了解元素交互的常用方法 2、学习如何对多选元素进行操作 3、 学习模拟鼠标进行相关操作 ( 本节目标 ) 4、 学习模拟键盘进行相关操作 ( 本节目标 ) 用于向 Web 浏览器提供虚拟化设备输入操作的低级接口.除了刚刚讲过的Web元素交互之外, Actions 接口 还提供了对指定输

    2024年03月24日
    浏览(38)
  • jenkins实现接口自动化持续集成(python+pytest+ Allure+git)

    jenkins实现接口自动化持续集成(python+pytest+ Allure+git)

    在用python做自动化测试时,我们写好代码,然后需要执行才能得到测试报告,这时我们可以通过 Jenkins 来进一步完成自动化工作。借助Jenkins,我们可以结合 Git/SVN 自动拉取代码,通过设置定时构建实现自动触发脚本执行,得到测试报告,最后还可以配置发送邮件等。今天我们

    2024年02月03日
    浏览(40)
  • Python、Pytest、Allure、Selenium和Jenkins实现自动化测试集成实例

    Python、Pytest、Allure、Selenium和Jenkins实现自动化测试集成实例

    下方查看历史精选文章 重磅发布 - 自动化框架基础指南pdfv1.1 大数据测试过程、策略及挑战 测试框架原理,构建成功的基石 在自动化测试工作之前,你应该知道的10条建议 在自动化测试中,重要的不是工具 本文将介绍如何使用Python、Pytest、Allure、Selenium和Jenkins实现测试自动

    2024年02月09日
    浏览(41)
  • python+pytest+selenium+PO+allure+DDT实现web自动化测试

    python+pytest+selenium+PO+allure+DDT实现web自动化测试

    python:编程语言 pytest:独立的、全功能的python单元测试框架 selenium:用于web应用程序测试的工具 allure:测试报告展示 ddt:数据驱动 1.1 python解释器 3.10版本 1.2 pycharm集成开发环境 社区版 下载浏览器驱动,浏览器驱动版本要与浏览器版本一致。 下载地址: Chrome:http://npm.ta

    2024年02月02日
    浏览(41)
  • UI自动化测试:Selenium+PO模式+Pytest+Allure整合

    UI自动化测试:Selenium+PO模式+Pytest+Allure整合

    本人目前工作中未涉及到WebUI自动化测试,但为了提升自己的技术,多学习一点还是没有坏处的,废话不多说了,目前主流的webUI测试框架应该还是selenium,考虑到可维护性、拓展性、复用性等,我们采用PO模式去写我们的脚本,本文档也主要整合了Selenium+PO模式+Pytest+Allure,下

    2024年02月08日
    浏览(202)
  • Web UI 自动化测试框架(Pytest+Selenium+Allure/Pytest-html+Loguru)

    本框架主要是基于 Python + pytest + selenium + Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的WEB UI自动化框架。 基于PageObject设计模式结合,该平台可实现测试用例的自动化执行及自动化测试报告的生成同时包括自动化测试执行时,用例失败的截图操作。 使用webdriver_manag

    2024年02月12日
    浏览(40)
  • Web UI 自动化测试框架(Pytest+Selenium+Allure+Loguru)

    本框架主要是基于 Python + pytest + selenium + Allure + loguru + 邮件通知/企业微信通知/钉钉通知 实现的WEB UI自动化框架。 基于PageObject设计模式结合,该平台可实现测试用例的自动化执行及自动化测试报告的生成同时包括自动化测试执行时,用例失败的截图操作。 使用webdriver_manag

    2024年02月04日
    浏览(153)
  • 【自动化测试】Pytest+Appium+Allure 做 UI 自动化的那些事

    【自动化测试】Pytest+Appium+Allure 做 UI 自动化的那些事

    文本主要介绍下 Pytest+Allure+Appium 记录一些过程和经历。 法主要用了啥: Python3 Appium Allure-pytest Pytest Appium 不常见却好用的方法 Appium 直接执行 adb shell 方法 #Appium 启动时增加 --relaxed-security 参数 Appium 即可执行类似adb shell的方法 appium -p 4723 --relaxed-security #使用方法 def adb_shell(se

    2024年01月25日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包