Playwright + Pytest 实现 Web UI 自动化测试

前言

平时工作一直用的 Cypress,但是 Cypress 真的太慢了,就想看看有什么替代品。Selenium 我一直不太喜欢,正好看到微软出的 Playwright 好像蛮有意思的。虽然 Playwright 的 JS 版本 的 Star 是 Python 版本的十倍之多,不过我个人还是更喜欢 Python 和 Pytest,就还是用 Python 版的 Playwright 搭配 Pytest 来食用了。(其实是 js 不太熟悉 XD)

主流的 UI 自动化 框架对比可以参考这篇文章 []

本项目风格和我之前写的 api-test比较相似,越小越简单越好。

项目地址

Github:

本项目参考了 & 的项目

分层

定位符、页面操作、业务逻辑三层:合并成了 page 类 测试用例层:test 类(基于 pytest) 数据层:yaml 文件 (config 数据 与 fixtures 数据) 结果层:HTML 报告(基于 pytest-html,感兴趣可以改成 allure),Log 日志(暂无)

* 图来自 陈晓伍 的 《Python Web自动化测试设计与实现》

test 类 page @pytest.fixture 来自 pytest-playwright env 是自定义的 @pytest.fixture,用来读取当前环境的配置信息 使用内置的 browser @pytest.fixture 来 生成一个 page @pytest.fixture 提升作用域,以达到共用同一浏览器的效果

page 类 想了好几种方式,如何来使用page对象。本来想试试继承,但好像不行。 最终还是作为类属性来使用了。

config 数据,管理 base_url 等通用信息 fixtures 数据,就是测试用例的测试数据了,只是我习惯 Cypress 的命名方式了。

fixtures 我定义了2种获取方式。 一种是test 类中获取当前要用的测试对象的 yaml 文件

# test 类
@pytest.fixture()
def fixtures(self):
    yield Tools.get_fixtures("baidu_search")


# ---- 用法 -----
# test 类
fixtures[kerwords]

一种是放在conftest.py中,作为@pytest.fixture 来用

# conftest.py
@pytest.fixture(scope="class")
def fixtures():
    def _fixtures(filename: str):
        from common.tools import Tools
        return Tools.get_fixtures(filename)

    yield _fixtures


# ---- 用法 -----
# test 类
fixtures(baidu_search)[kerwords]

其他

虚拟环境使用的 poetry

conftest.py 文件中,pytest 钩子和 pytest-html 钩子都有详尽的注解

报告 用的 pytest-html,我还蛮喜欢的。

新功能

视频录制

Playwright 内置的视频录制,传入一个 路径就可以了。 还学到了一个新钩子

@pytest.fixture(scope="session")
def browser_context_args(browser_context_args, tmpdir_factory: pytest.TempdirFactory):
    return {
          
   
        **browser_context_args,
        "record_video_dir": os.path.join(OUTPUT_DIR, "videos")
        }

allure 报告

在 feature/allure 分支

已知问题

  1. config 数据 和 fixtures 数据如何整合,目前还在思考
  2. Tools.get_config()、Tools.get_fixtures() 等依赖 env 信息的操作,只能放在__init__ 或 实例方法中。因为引入文件时会自动执行文件。此时还没有 env 信息,所以会报错。
# 错误
class BaiduSearchPage:

        host = Tools.get_config("base_url")
        
    def __init__(self, page: Page):
        self.page = page

# 正确
class BaiduSearchPage:

    def __init__(self, page: Page):
        self.page = page
        self.host = Tools.get_config("base_url")
经验分享 程序员 微信小程序 职场和发展