playwright一直是我最看好的新一代自动化测试框架,2022年底playwright在npm上的下载量超过了100万,尽管不如selenium和cypress,不过势头还是相当强劲的。最近正好发现一篇文章简单的介绍了使用typescript,pageobject和fixture配合playwright进行用例编写的文章,这里把里面的精华拿出来分享一下。
框架整体的目录结构如下。
.
├── config
│ ├── global-setup.ts
│ └── playwright.config.ts
├── package-lock.json
├── package.json
└── src
├── data
│ └── data.json
├── fixtures
│ ├── AxeFixture.ts
│ └── TodoFixture.ts
├── pages
│ └── TodoPage.ts
└── tests
├── a11y.spec.ts
└── demo-pom-todo-app.spec.ts
playwright.config.ts playwright的配置文件
global-setup.ts 在所有用例执行前运行一次,主要的目的是登录一次被测系统并保存浏览器的全局状态到storageState.json文件中。这样就不需要每个用例都去单独登录一次了。更多信息可以参考文档。https://playwright.dev/docs/test-advanced#global-setup-and-teardown
po基本上是自建框架的必选项了。具体的实现如下
import { expect, Locator, Page } from '@playwright/test';
export class TodoDemoPage {
readonly page: Page;
readonly newTodoInput: Locator;
readonly todoTitle: Locator;
readonly todoCount: Locator
constructor(page: Page) {
this.page = page;
this.newTodoInput = page.getByPlaceholder('What needs to be done?');
this.todoTitle = page.getByTestId('todo-title');
this.todoCount = page.getByTestId('todo-count');
}
async goto() {
await this.page.goto('https://demo.playwright.dev/todomvc');
}
async addTodo(data: string) {
await this.newTodoInput.fill(data)
await this.newTodoInput.press("Enter")
}
async addDefaultTodos(todosItems: string[]) {
for (const todo of todosItems) {
await this.addTodo(todo)
}
}
...
}
关于po需要注意几点
fixture可以简单理解为准备数据,设置上下文环境
import { test as base } from '@playwright/test';
import { TodoDemoPage } from '../pages/TodoPage';
type MyFixtures = {
todoDemoPage: TodoDemoPage;
noneExistingPage: any
};
export const todoDemoPage = async({page}, use) => {
const todoDemoPage = new TodoDemoPage(page);
// Set up the fixture.
await todoDemoPage.goto();
// Use the fixture value in the test.
await use(todoDemoPage);
}
// we can create as many fixtures as we want, but I prefer to store them in separate files
export const noneExistingPage = async({page}, use) => {
// Let's imagine we have another fixter-page set up here
}
export const test = base.extend({todoDemoPage, noneExistingPage});
上面的代码其实就是创建了TotoDemoPage,后面用例里就可以直接使用这个页面了。
fixture的好处还是很多的。
fixture让setup和teardown钩子函数在同一个地方进行定义,这样就比较好维护了
用例相对就比较简单的,因为难的部分已经搞完了。
import { test } from '../fixtures/TodoFixture'
const TODO_ITEMS = [
'buy some cheese',
'buy bottle of wine, or two',
'celebrate'
];
// our test is imported from fixtures folder
// so we can have access to tododDempPage and noneExistingPage objects
// in callback function trhough destructuring and we can use it for our needs
test.describe('New Todo', () => {
test('should allow me to add todo items', async ({ todoDemoPage }) => {
await todoDemoPage.addTodo(TODO_ITEMS[0])
await todoDemoPage.checkInputIsEmpty();
await todoDemoPage.addTodo(TODO_ITEMS[1])
await todoDemoPage.checkAddedTodos([TODO_ITEMS[0], TODO_ITEMS[1]])
await todoDemoPage.checkNumberOfTodosInLocalStorage(2);
});
test('should clear text input field when an item is added', async ({ todoDemoPage }) => {
await todoDemoPage.addTodo(TODO_ITEMS[0])
await todoDemoPage.checkInputIsEmpty();
await todoDemoPage.checkNumberOfTodosInLocalStorage(1);
});
test('should append new items to the bottom of the list', async ({ todoDemoPage }) => {
await todoDemoPage.addDefaultTodos(TODO_ITEMS);
await todoDemoPage.checkDefaultAddedTodods(TODO_ITEMS);
await todoDemoPage.checkAddedTodos(TODO_ITEMS)
await todoDemoPage.checkNumberOfTodosInLocalStorage(3);
});
});
用例很简洁易懂对吧。这里断言都封装在了page object里,所以整个流程全是对po实例进行调用,很统一,不过我不是很喜欢这种方式,我更喜欢把原生断言放在用例里,这样po层会更简洁一些,要不需要绞尽脑汁去给封装断言的方法取名。
可以在官方文档找到方法。https://playwright.dev/docs/ci
https://github.com/eugeniuszG/playwright-starter 这里有框架模板,大家可以下载下来进行二次开发,最后感谢作者的总结和示例。
最后: 下方这份完整的软件测试视频学习教程已经整理上传完成,朋友们如果需要可以自行免费领取 【保证100%免费】
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!