「自动化测试」playwright 前端自动化—— fixtures 项目重构优化思路

「自动化测试」playwright 前端自动化—— fixtures 项目重构优化思路在开始准备重构时 我们项目都是基于对象模型去抽象公用方法在做设计之时 会根据各个模块建立对应的模型 起初这个方法很好用 很容易上手 代码也很好理解 基于面向对象封装 例如 封装一个新增事件的网络请求方法该方法许多用例都需要用到 故选择封装那么实现逻辑 问题出现当越封越多的模块 那么编写一个用例所需要创建的实例则越来越多例如 可以看到 这将会造成每一个页面每一个测试用例就会编写很多重复的代码 且页面也会变得很长 变得难以维护和不堪入目什么是 fixtures 简而言之 就是当引入后 你将不用受和重 playwrightfi

fixtures 重构项目

在开始准备重构时,我们项目都是基于对象模型去抽象公用方法

页面对象模型

将页面内的公用对象和公用方法封装起来放到一个模型中,我们把这个模型叫做页面对象模型。是UI自动化测试中业务逻辑的复用和简化的重要设计方法;

页面对象模型提取的原则

  • 可以被复用或者将要被复用的素选中、快捷方法
  • 比较复杂操作可以考虑提取到页面对象模型(也可以是提取单独的文件),较少业务用例代码的负责度(我们希望是开发输出页面对象模型,测试来输出业务用例)
  • 公共组件的素选中、方法,如表格、弹窗、抽屉等组件

做设计之时,会根据各个模块建立对应的模型,起初这个方法很好用,很容易上手代码也很好理解(基于面向对象封装)

例如:封装一个新增事件的网络请求方法

该方法许多用例都需要用到,故选择封装

那么实现逻辑

import { 
    APIRequestContext, APIResponse } from '@playwright/test'; class Modal { 
    constructor (readonly request: APIRequestContext) // xxx请求 public async post (url: string, options?: PostOptions): Promise<APIResponse> { 
    return this.request.post(`${ 
     baseUrl.default}${ 
     url}`, { 
    ignoreHTTPSErrors: true, ...options, headers: { 
    'content-type': 'application/json', ...options?.headers } }); } } 

问题出现

当越封越多的模块,那么编写一个用例所需要创建的实例则越来越多

例如

import { 
    test } from '@playwright/test'; import { 
    X } from './X'; import { 
    XX } from './XX'; import { 
    XXX } from './XXX'; import { 
    XXXX } from './XXXX'; import { 
    XXXXX } from './XXXXX'; test.describe('just a temple', () => { 
    test('just a temple', async ({ 
   page}) => { 
    const xInstance = new X(page); const xxInstance = new XX(page); const xxxInstance = new XXX(page); const xxxxInstance = new XXXX(page); const xxxxxInstance = new XXXXX(page); }); test('test2 xx', async () => { 
    // 类似的重复操作 }); }); 

可以看到,这将会造成每一个页面每一个测试用例就会编写很多重复的代码,且页面也会变得很长,变得难以维护和不堪入目

fixtures 引入

什么是fixtures

Playwright Test 基于fixtures的概念。fixtures用于为每个测试建立环境,为测试提供所需的一切,仅此而已。fixtures在测试之间是隔离的。使用fixtures,您可以根据它们的含义对测试进行分组,而不是根据它们的通用设置。

简而言之,就是当引入 fixtures 后,你将不用受 describe 和重复代码的烦恼,可以自用的根据 fixtures 语意去分组,去进行测试用例编写

代码优化

以上代码经过优化后:

// 封装为公共方法 utils/fixtures import { 
    test as base } from '@playwright/test'; import { 
    X } from './X'; import { 
    XX } from './XX'; import { 
    XXX } from './XXX'; import { 
    XXXX } from './XXXX'; import { 
    XXXXX } from './XXXXX'; type TestFixtures = { 
    x: X; xx: XX; xxx: XXX; xxxx: XXXX; xxxxx: XXXXX; } // 将封装后的test导出 export const test = base.extend<TestFixtures>({ 
    x: async ({ 
   page}}, use) => { 
    await use(new X(page)); }, xx: async ({ 
   page}}, use) => { 
    await use(new XX(page)); }, xxx: async ({ 
   page}}, use) => { 
    await use(new XXX(page)); }, xxxx: async ({ 
   page}}, use) => { 
    await use(new XXXX(page)); }, xxxxx: async ({ 
   page}}, use) => { 
    await use(new XXXXX(page)); }, }); 

在页面中使用:

import { 
    test } from 'utils/fixtures'; test.describe('just a temple', () => { 
    test('just a temple', async ({ 
   x, xx, xxx, xxxx, xxxxx}) => { 
    // 只需书写业务代码 }); test('test2 xx', async (x, xx, xxx, xxxx, xxxxx) => { 
    // 只需书写业务代码 }); )}; 

经过更改后的代码,减少了一大堆重复代码,且使用起来更加方便代码也看着更舒服!

分组优化

优化前:使用 describe 来为 test 增加前置后置操作

// todo.spec.js const { 
    test } = require('@playwright/test'); const { 
    TodoPage } = require('./todo-page'); test.describe('todo tests', () => { 
    let todoPage; // 前置操作 test.beforeEach(async ({ 
    page }) => { 
    todoPage = new TodoPage(page); await todoPage.goto(); await todoPage.addToDo('item1'); await todoPage.addToDo('item2'); }); // 后置操作 test.afterEach(async () => { 
    await todoPage.removeAll(); }); test('should add an item', async () => { 
    await todoPage.addToDo('my item'); // ... }); test('should remove an item', async () => { 
    await todoPage.remove('item1'); // ... }); }); 

优化后:不需要再添加不必要的 describetest用例可以更加自由,灵活

// example.spec.ts import { 
    test as base } from '@playwright/test'; import { 
    TodoPage } from './todo-page'; // 封装test提供 todoPage fixtures const test = base.extend<{ 
      todoPage: TodoPage }>({ 
    todoPage: async ({ 
    page }, use) => { 
    // 前置操作 const todoPage = new TodoPage(page); await todoPage.goto(); await todoPage.addToDo('item1'); await todoPage.addToDo('item2'); // fixtures 使用值 await use(todoPage); // 后置操作 await todoPage.removeAll(); }, }); test('should add an item', async ({ 
    todoPage }) => { 
    await todoPage.addToDo('my item'); // ... }); test('should remove an item', async ({ 
    todoPage }) => { 
    await todoPage.remove('item1'); // ... }); 

遵循页面对象模型模式的fixtures封装todoPagesettingsPage

// my-test.ts import { 
    test as base } from '@playwright/test'; import { 
    TodoPage } from './todo-page'; import { 
    SettingsPage } from './settings-page'; // 定义fixtures的类型 type MyFixtures = { 
    todoPage: TodoPage; settingsPage: SettingsPage; }; // 导出封装好的test export const test = base.extend<MyFixtures>({ 
    todoPage: async ({ 
    page }, use) => { 
    // fixtures 注册前行为 const todoPage = new TodoPage(page); await todoPage.goto(); await todoPage.addToDo('item1'); await todoPage.addToDo('item2'); // 在测试中使用该值 await use(todoPage); // fixtures 卸载后行为 await todoPage.removeAll(); }, settingsPage: async ({ 
    page }, use) => { 
    await use(new SettingsPage(page)); }, }); 

落实项目

将公共模块的页面对象模型全部封装,并应用到项目中

/ * @author: hwc13375 * @description: fixtures */ import { 
    test as base } from '@playwright/test'; import { 
    RequestCommon } from './request'; import { 
    GlobalCommon, ConditionFilter, Drawer, Message, Modal, Table } from 'e2e-modules/models'; type TestFixtures = { 
    requestCommon: RequestCommon; globalCommon: GlobalCommon; conditionFilter: ConditionFilter; drawer: Drawer; message: Message; modal: Modal; table: Table; }; export const test = base.extend<TestFixtures>({ 
    requestCommon: async ({ 
   request, browser}, use) => { 
    await use(new RequestCommon(request, browser)); }, globalCommon: async ({ 
   page}, use) => { 
    await use(new GlobalCommon(page)); }, conditionFilter: async ({ 
   page}, use) => { 
    await use(new ConditionFilter(page)); }, drawer: async ({ 
   page}, use) => { 
    await use(new Drawer(page)); }, message: async ({ 
   page}, use) => { 
    await use(new Message(page)); }, modal: async ({ 
   page}, use) => { 
    await use(new Modal(page)); }, table: async ({ 
   page}, use) => { 
    await use(new Table(page)); }, }); 

最终结果

根据重构提交 pr 查看,平均一个场景减少50行左右的重复代码
一共优化了5个场景200+行的代码减少,使页面代码更简洁易维护,改变了以往的开发模式

今天的文章 「自动化测试」playwright 前端自动化—— fixtures 项目重构优化思路分享到此就结束了,感谢您的阅读。
编程小号
上一篇 2024-12-13 19:33
下一篇 2024-12-13 19:30

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/85134.html