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'); // ... }); });
优化后:不需要再添加不必要的 describe
,test
用例可以更加自由,灵活
// 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
封装todoPage
,settingsPage
// 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+行的代码减少,使页面代码更简洁易维护,改变了以往的开发模式
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/bian-cheng-ji-chu/85134.html