在2023使用playwright进行自动化测试
playwright一直是我最看好的新一代自动化测试框架,2022年底playwright在npm上的下载量超过了100万,尽管不如selenium和cypress,不过势头还是相当强劲的。最近正好发现一篇文章简单的介绍了使用typescript,pageobject和fixture配合playwright进行用例编写的文章,这里把里面的精华拿出来分享一下。
老生常谈,playwright的优势
- 有个好爹,微软出品,看好长期更新维护和迭代,但也可能突然被砍掉,毕竟大公司都在裁员
- 运行速度快
- 自动等待元素出现
- 报告的呈现很多元化,可以设置重试机制,捕获执行日志,截屏录屏等
- 支持多个浏览器并行执行
- 提供自动生成代码能力以及Inspector GUI
- 一套代码,跨浏览器执行的能力
目录结构
框架整体的目录结构如下。
.
├── 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
config目录
- playwright.config.ts playwright的配置文件
- ***global-setup.ts*** 在所有用例执行前运行一次,主要的目的是登录一次被测系统并保存浏览器的全局状态到storageState.json文件中。这样就不需要每个用例都去单独登录一次了。更多信息可以参考文档。https://playwright.dev/docs/test-advanced#global-setup-and-teardown
Page Object
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需要注意几点
- 命名规则,确保页面上的元素和一些页面方法都有合适的名称
- 一个方法只做一件事情,而且可以通过方法名推测出来
- 对页面呈现的一些结果进行断言
- dry,don’t repeat yourself
Fixtures
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<MyFixtures>({todoDemoPage, noneExistingPage});
上面的代码其实就是创建了TotoDemoPage,后面用例里就可以直接使用这个页面了。
fixture的好处还是很多的。
- fixture让setup和teardown钩子函数在同一个地方进行定义,这样就比较好维护了
- fixture可以重复使用
- fixture按需使用,定义了你也可以不用
- fixture可以组合使用
测试用例
用例相对就比较简单的,因为难的部分已经搞完了。
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层会更简洁一些,要不需要绞尽脑汁去给封装断言的方法取名。
CICD
可以在官方文档找到方法。https://playwright.dev/docs/ci
总结
https://github.com/eugeniuszG/playwright-starter 这里有框架模板,大家可以下载下来进行二次开发,最后感谢作者的总结和示例。