BDD Cucumber 结合 Puppeteer 做 Web 自动化

imzack · 2019年05月14日 · 2059 次阅读

主要内容

  • Puppeteer 简介
  • 快速入门
  • 结合 Cucumber 做 UI 自动化

很多人听说过用 Selenium/WebDriver 做 Web 自动化,今天我们介绍一种比 Selenium/WebDriver 运行更快速的 Web 自动化方式,Puppeteer。

Puppeteer 简介

Puppeteer 是 NPM 库,它提供了 NodeJS 高级 API 来控制 Chrome。Puppeteer 默认以无头(无界面)方式运行,但也可以配置为运行有界面的 Chrome。

使用 Puppteer 可以自动化在浏览器中手动执行的大多数事情。比如:

  • 生成页面的屏幕截图和 PDF。
  • 抓取 SPA(单页面应用程序)并生成预渲染内容(即 “SSR”(服务器端渲染)。
  • 自动化表单提交、UI 测试、键盘输入等。
  • 创建最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能直接在最新版本的 Chrome 中运行测试。
  • 捕获网站的时间线跟踪,以帮助诊断性能问题。
  • 测试 Chrome 扩展程序。

快速入门

安装

要在项目中使用 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 框架做 UI 自动化

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

暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册