很多人听说过用 Selenium/WebDriver 做 Web 自动化,今天我们介绍一种比 Selenium/WebDriver 运行更快速的 Web 自动化方式,Puppeteer。
Puppeteer 是 NPM 库,它提供了 NodeJS 高级 API 来控制 Chrome。Puppeteer 默认以无头(无界面)方式运行,但也可以配置为运行有界面的 Chrome。
使用 Puppteer 可以自动化在浏览器中手动执行的大多数事情。比如:
要在项目中使用 Puppeteer,请运行:
$ npm i puppeteer
或者
$ yarn add puppeteer
备注:由于安装时会下载 Chromium,默认使用国外服务器下载,速度会比较慢。 在下载之前可以配置使用 taobao 镜像地址下载。
$ npm config set puppeteer_download_host=https://npm.taobao.org/mirrors
### 用法
与其他浏览器测试框架原理一样。首先调用 Puppeteer 的 API 创建一个 Browser 实例并打开页面,然后使用 Puppeteer 的 API 对它们进行操作。
示例 - 导航到https://example.comexample.png:并将截屏保存为
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
await browser.close();
})();
将文件保存到为 example.js 并调用"node example.js"
命令执行它。
Puppeteer 默认将初始页面大小设置为 800px * 600px,它定义了屏幕截图大小。页面大小可以通过 Page.setViewport() 自定义。
Puppeteer 更多 API,请参考:https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md
Cucumber 用自然语言结合 JavaScript 编写的自动化测试脚本。因为它们是用自然语言编写的,所以你团队中的任何人都可以阅读它们,而且可以用它们来帮助改善团队之间的沟通、协作和信任。
更多关于 Cucumber 的介绍,可以参考之前的文章。
我们用 http://todomvc.com/examples/react/#/ 网站作为被测应用示例:
这是一个用来记录待做事项的页面。
在文本输入框中输入文本 hello world,按下 enter 键,将会创建一个新的 todo。
以上可以做我们这次 demo 的场景,下面带大家一步一步操作来实现操作。
你可以手动创建项目基本的目录,或者直接使用 CukeTest 创建一个 basic 项目模版。它生成如下典型的 Cucumber 项目文件结构。
|____features
| |____page.feature
| |____step_definitions
| | |____definitions1.js
|____package.json
编辑 feature 文件,写下我们的操作步骤:
# language: zh-CN
功能: TODO 应用添加应用测试
作为用户,我可以在http://todomvc.com/examples/react/#/
创建一些todo 列表
场景: 添加一项新的TODO
假如我今天必须完成一些新的TODO"打扫房间"
当我把这些TODO输入到输入框中
而且我按下Enter键
那么我期望这些TODO在我的TODO列表中
你可以使用在 CukeTest 的文本界面中粘贴上面的 feature 文本,然后可以切换到可视化界面,方便可视化操作,如生成代码、拖拽等。
打开 definitions1.js 文件,在 CukeTest 中可视化界面中点击每个操作步骤后面的灰色按钮,即可快速生成自动化代码样例。如下图,逐个点击每个步骤上的按钮会生成 JavaScript 的代码框架:
完全生成的代码框架如下:
step_definitons/definitions1.js
var { Given, When, Then } = require('cucumber')
Given("我今天必须完成一些新的TODO{string}", async function (arg1) {
return 'pending';
});
When("我把这些TODO输入到输入框中", async function () {
return 'pending';
});
When("我按下Enter键", async function () {
return 'pending';
});
Then("我期望这些TODO在我的TODO列表中", async function () {
return 'pending';
});
上面每个步骤定义函数的第一个参数用来匹配步骤文本,并提取参数,第二个函数参数是步骤定义的实现。
实现了这些步骤,就完成了自动化脚本。
我们知道,Web 自动化有个 Page Object 模式,就是把页面相关的操作封装在一个类中,便于调用。我们在这里封装了一个 Cucumber World 对象,包含了 Page 相关的操作。World 对象是 Cucumber 的概念,一个场景执行的时候会自动生成一个 World 对象。详细定义可以参考 World 定义(http://cuketest.com/zh-cn/cucumber/concepts.html#world)。
创建 world.js,使用 Puppeteer 相关 API 实现场景定义中的操作,将操作代码封装到 class 中。
step_definitions/world.js
const { setWorldConstructor } = require("cucumber");
const { expect } = require("chai");
const puppeteer = require("puppeteer");
const PAGE = "http://todomvc.com/examples/react/#/";
class TodoWorld {
constructor({attach, parameters}) {
this.todo = "";
this.attach = attach;
this.parameters = parameters;
}
async openTodoPage() {
this.browser = await puppeteer.launch();
this.page = await this.browser.newPage();
await this.page.goto(PAGE);
}
setTodo(todo) {
this.todo = todo;
}
async writeTodo() {
const inputSelector = "section input";
await this.page.waitForSelector(inputSelector);
this.inputElement = await this.page.$(inputSelector);
await this.inputElement.type(this.todo);
}
async submit() {
await this.inputElement.press("Enter");
}
async checkTodoIsInList() {
const todoSelector = "ul.todo-list li label";
await this.page.waitForSelector(todoSelector);
const todo = await this.page.evaluate(
todoSelector => document.querySelector(todoSelector).innerText,
todoSelector
);
expect(this.todo).to.eql(todo);
}
async takeScreen(){
return await this.page.screenshot({ encoding:'base64' });
}
async closeTodoPage() {
await this.browser.close();
}
}
setWorldConstructor(TodoWorld);
其中,Cucumber 中自定义的 World 对象要通过 setWorldConstructor 设置到 Cucumber 中,才会在执行的过程中被创建。
有了 World 对象,我们在步骤定义的框架中实现步骤就简便多了,只需要通过"this.” 的方式调用 world 对象,访问这个对象的方法。
下面是步骤定义实现后的代码:
step_definitons/definitions1.js
const { Given, When, Then,Before,After } = require("cucumber");
Before(async function () {
return await this.openTodoPage();
});
After(async function(){
let png = await this.takescreen();
this.attach(png, 'image/png');
return await this.closeTodoPage();
})
Given("我今天必须完成一些新的TODO{string}", function (todo) {
this.setTodo(todo);
});
When("我把这些TODO输入到输入框中", async function () {
return await this.writeTodo();
});
When("我按下Enter键", async function () {
return await this.submit();
});
Then("我期望这些TODO在我的TODO列表中", async function () {
return await this.checkTodoIsInList();
});
在 After hook 中,定义了截屏的操作,这个操作会将截屏内容放入报表。因为 After hook 会在每个场景结束的时候被调用,所以每个场景操作后的截屏会放入执行报告中。
在 CukeTest 中运行项目,即可生成自动化报告,并且运行完成后有页面截图:
更多 Cucumber 的相关知识可以访问:
https://github.com/cucumber/cucumber-js
Puppeteer 的开源项目:
https://github.com/GoogleChrome/puppeteer#readme