使用Cucumber × Puppeteer × chai 搭建E2E测试执行环境在BDD开发中

我进行了E2E测试环境的搭建和样例实现。

E2E测试是端到端测试。

为了测试用户在使用服务时的视角,将系统运行起来,以接近实际运行时的形式。这次我们假设是在Web上进行测试,并搭建了一个能在浏览器中运行的端对端测试环境。

E2E测试(集成测试)的优点与缺点的参考网址。

※这次考试的一部分将使用以下网页:

Welcome to NGINX!

使用工具

小黄瓜.js

Cucumber-JS是一个处理BDD(行为驱动开发)框架的JavaScript库。通过在称为feature的文件中编写用户行为并基于这些行为来执行系统操作。
使用Gherkin语言来描述这些行为。
GitHub链接:https://github.com/cucumber/cucumber-js

如果对BDD不太了解的话,下面的文章可能会有所帮助:
参考链接:https://www.atmarkit.co.jp/ait/articles/1403/25/news033.html

木偶师 (Mù Ǒu Shī)

Puppeteer是一个用于操作Chrome和无头Chrome的库。
与Selenium类似,但是Puppeteer不需要单独安装Web Driver。
GitHub:https://github.com/puppeteer/puppeteer

拆解 jiě)

这是一个用于测试断言的库。在这次使用中,它将用于检查特定HTML标签内的字符串是否符合预期。GitHub链接:https://github.com/chaijs/chai

环境搭建步骤

切换 Node.js 版本

我使用了Nodebrew,将版本升级至v13.9.0。

$ nodebrew install v13.9.0
$ nodebrew use v13.9.0

创建package.json

这次使用yarn进行软件包安装。

$ yarn init

安装各种软件包

我要安装puppeteer、cucumber和chai。

$ yarn add puppeteer --dev
$ yarn add cucumber --dev
$ yarn add chai --dev

添加执行命令的别名

在package.json中添加用于从cucumber运行测试的命令。

{
  "name": "e2e_test",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "scripts": {
    "e2e_test": "./node_modules/.bin/cucumber-js" // 追加
  },
  "devDependencies": {
    "chai": "^4.2.0",
    "cucumber": "^6.0.5",
    "puppeteer": "^2.1.1"
  }
}

启动用于E2E测试的服务器。

此次我们将以nginx服务器为例进行部署。
显示默认页面,并执行使用该页面及其后续页面进行的端到端测试。
从另一个终端启动服务器的Docker容器。

$ docker run -p 8080:80 nginx

图像呈现

スクリーンショット 2020-02-29 0.20.57.png
スクリーンショット 2020-02-29 0.22.03.png

现在我们来编写一个测试用例,其中将用户行为设定为到目前为止的动作。下面是设定的场景:

    • シナリオ名: nginxの初期表示画面から公式ページに飛ぶことができる

 

    • 前提条件: nginxの初期表示画面が表示されている

 

    • アクション: nginx.com のリンクをクリックする

 

    結果: 遷移したページに Welcome to NGINX! が表示されている。

实施

特征文件

首先,创建一个名为 features 的目录,并创建一个名为 nginx.feature 的文件。
在这个文件中,编写用户要遵循的场景。

Feature: nginx画面
  エンドユーザーがnginxの様々なページで動作を行うシナリオ

  Scenario: nginxの初期画面から公式ページに飛ぶことができる
    Given nginxの初期画面が表示されている
    When "nginx.com" のリンクをクリックする
    Then 遷移したページに "Welcome to NGINX!" が表示されている

在执行cucumber.js时,将自动加载features目录,并自动执行其中的〇〇.feature文件。

当以当前状态执行时,会在控制台上显示“没有为场景定义的实现部分”这样的警告信息。

$ ./node_modules/.bin/cucumber-js
UUU

Warnings:

1) Scenario: nginxの初期画面から公式ページに飛ぶことができる # features/nginx.feature:4
   ? Given nginxの初期画面が表示されている
       Undefined. Implement with the following snippet:

         Given('nginxの初期画面が表示されている', function () {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });

   ? When "nginx.com" のリンクをクリックする
       Undefined. Implement with the following snippet:

         When('{string} のリンクをクリックする', function (string) {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });

   ? Then 遷移したページに "Welcome to NGINX!" が表示されている
       Undefined. Implement with the following snippet:

         Then('遷移したページに {string} が表示されている', function (string) {
           // Write code here that turns the phrase above into concrete actions
           return 'pending';
         });


1 scenario (1 undefined)
3 steps (3 undefined)
0m00.000s
error Command failed with exit code 1.

只需将显示的片段复制到用于实施操作的文件中即可。

实施行动

创建一个名为”step_definitions”的目录,并在其中创建一个名为”nginx_steps.js”的文件。
将先前显示的代码段粘贴到该文件中。
不要忘记添加”Given”等方法的导入。

const { Given, When, Then } = require("cucumber");

Given('nginxの初期画面が表示されている', function () {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

When('{string} のリンクをクリックする', function (string) {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

Then('遷移したページに {string} が表示されている', function (string) {
  // Write code here that turns the phrase above into concrete actions
  return 'pending';
});

这个就可以了。
当执行时,与之前不同的是,一旦给出结果为”Pending”,测试就会停止。

$ yarn run e2e_test
yarn run v1.22.0
$ ./node_modules/.bin/cucumber-js
P--

Warnings:

1) Scenario: nginxの初期画面から公式ページに飛ぶことができる # features/nginx.feature:4
   ? Given nginxの初期画面が表示されている # features/step_definitions/nginx_steps.js:3
       Pending
   - When "nginx.com" のリンクをクリックする # features/step_definitions/nginx_steps.js:8
   - Then 遷移したページに "Welcome to NGINX!" が表示されている # features/step_definitions/nginx_steps.js:13

1 scenario (1 pending)
3 steps (1 pending, 2 skipped)
0m00.000s
error Command failed with exit code 1.

顺便提一下,在cucumber的场景中,用双引号括起来的部分表示导航到的页面上显示了”Welcome to NGINX!”。
因此,在执行场景时,这部分将作为参数传递给方法。
因此,在step_definitions/nginx_steps.js中,When和Then方法都有相应的部分作为{string}参数进行定义。

操纵木偶的实现方法

鉴于

首先,这是nginx的初始页面。
我先用puppeteer编写了一个展示页面的部分。

const { Given, When, Then } = require("cucumber");
const puppeteer = require('puppeteer');

var page = null;

Given('nginxの初期画面が表示されている', async function () { // awaitを使うのでasyncを追加。
  const browser = await puppeteer.launch({headless: false}); // ブラウザを表示するのでheadlessをfalseに
  page = await browser.newPage();
  await page.goto('http://localhost:8080'); // http://localhost:8080に遷移
});

// 以下省略
スクリーンショット 2020-02-29 0.54.33.png

然而,在这种情况下,无法保证“显示的是nginx的初始页面”在给定中。因此,对于显示在屏幕上的元素进行断言。

const { Given, When, Then } = require("cucumber");
const puppeteer = require('puppeteer');
const { assert } = require('chai');

var page = null;

Given('nginxの初期画面が表示されている', async function () {
  const browser = await puppeteer.launch({headless: false});
  page = await browser.newPage();
  await page.goto('http://localhost:8080');

  // h1タグ内の文字列を取得
  const h1Text = await page.$eval('h1', item => {
    return item.textContent;
  }); 

  assert.equal(h1Text, 'Welcome to nginx!');  // h1タグ内の文字列が「Welcome to nginx!」であるかのアサーション
});


// 以下省略

现在可以实现对Given内的检查。当执行时,Given的测试通过,而When则变为待定。

$ yarn run e2e_test
yarn run v1.22.0
$ ./node_modules/.bin/cucumber-js
.P-

Warnings:

1) Scenario: nginxの初期画面から公式ページに飛ぶことができる # features/nginx.feature:4
   ✔ Given nginxの初期画面が表示されている # features/step_definitions/nginx_steps.js:5
   ? When "nginx.com" のリンクをクリックする # features/step_definitions/nginx_steps.js:18
       Pending
   - Then 遷移したページに "Welcome to NGINX!" が表示されている # features/step_definitions/nginx_steps.js:23

1 scenario (1 pending)
3 steps (1 pending, 1 skipped, 1 passed)
0m01.351s

it rains, the grass grows.

点击「nginx.com」。

// Givenは省略

When('{string} のリンクをクリックする', async function (linkText) {
    const linkXpath = `//a[text() = "${linkText}"]`; // nginx.comリンクのxpathを取得
    await page.waitForXPath(linkXpath);
    await (await page.$x(linkXpath))[0].click(); // リンクをクリック
});

// 以下両略
スクリーンショット 2020-02-29 1.14.30.png

还有,我们已经根据以下参考实施了使用 Puppeteer 的 innerText 方法来选择具有指定文本的链接的点击方式。

然后

最后了。
我们可以通过点击(click())方法来确保链接跳转到官方网页,然后检查跳转页面是否显示了“Welcome to NGINX!”。
顺便一提,初始页面的nginx是小写字母,而官方页面的NGINX是大写字母。

// 省略

Then('遷移したページに {string} が表示されている', async function (pageTitleText) {
    await page.waitForNavigation({ waitUntil: "domcontentloaded" }); // ページのローディングが終わるまで待つ

    // h1タグ内の文字列を取得
    const h1Text = await page.$eval('h1', item => {
        return item.textContent;
    }); 

  assert.equal(h1Text, pageTitleText);  // h1タグ内の文字列が「Welcome to NGINX!」であるかのアサーション
});

在这个状态下执行,执行所有条件和断言,直到完成所有测试,输出测试完成。

$ yarn run e2e_test
yarn run v1.22.0
$ ./node_modules/.bin/cucumber-js
...

1 scenario (1 passed)
3 steps (3 passed)
0m04.815s

以上是实施和确认的结束!

已完成的文件 (Yǐ de

特点/ Nginx 特点特性.

Feature: nginx画面
  エンドユーザーがnginxの様々なページで動作を行うシナリオ

  Scenario: nginxの初期画面から公式ページに飛ぶことができる
    Given nginxの初期画面が表示されている
    When "nginx.com" のリンクをクリックする
    Then 遷移したページに "Welcome to NGINX!" が表示されている

step_definitions/nginx_steps.js 测试步骤定义/nginx_steps.js

const { Given, When, Then } = require("cucumber");
const puppeteer = require('puppeteer');
const { assert } = require('chai');

var page = null;

Given('nginxの初期表示画面が表示されている', async function () {
  const browser = await puppeteer.launch({headless: false});
  page = await browser.newPage();
  await page.goto('http://localhost:8080');

  // h1タグ内の文字列を取得
  const h1Text = await page.$eval('h1', item => {
    return item.textContent;
  }); 

  assert.equal(h1Text, 'Welcome to nginx!');  // h1タグ内の文字列が「Welcome to nginx!」であるかのアサーション
});

When('{string} のリンクをクリックする', async function (linkText) {
    const linkXpath = `//a[text() = "${linkText}"]`; // nginx.comリンクのxpathを取得
    await page.waitForXPath(linkXpath);
    await (await page.$x(linkXpath))[0].click(); // リンクをクリック
});

Then('遷移したページに {string} が表示されている', async function (pageTitleText) {
    await page.waitForNavigation({ waitUntil: "domcontentloaded" }); // ページのローディングが終わるまで待つ

    // h1タグ内の文字列を取得
    const h1Text = await page.$eval('h1', item => {
        return item.textContent;
    }); 

  assert.equal(h1Text, pageTitleText);  // h1タグ内の文字列が「Welcome to NGINX!」であるかのアサーション
});

最后

使用BDD框架可以自动输出方法来处理行为,以简明的方式实现而不需要花费太多工时。此外,使用BDD框架编写测试的话,将成为代表功能的文档,描述用户服务的特性文件,这在团队开发中有助于更容易认识产品核心功能。

请以此为参考。

广告
将在 10 秒后关闭
bannerAds