JavaScript中的BDD:黄瓜和小黄瓜入门

JavaScript中的BDD:黄瓜和小黄瓜入门

到目前为止,每个人都听说过测试驱动开发 (TDD),以及它对您的产品和开发生命周期的好处。 真是轻而易举。 每次为一段代码编写测试时,您都知道该代码有效。 而且,此外,您将来会知道该代码是否中断。

行为驱动开发(BDD)是对该概念的扩展,但是您不是在测试代码 ,而是在测试您的产品 ,特别是您的产品行为符合您的期望。

在本文中,我将向您展示如何使用Cucumber进行安装和运行,该框架运行以BDD风格编写的自动验收测试。 这些测试的优势在于,它们可以用简单的英语编写,因此参与项目的非技术人员可以理解。 阅读后,您将可以决定Cucumber是否适合您和您的团队,并开始编写自己的验收测试。

准备? 然后让我们潜入。

BDD vs TDD —那么,有什么区别?

主要以测试的结构和编写方式。

在TDD设置中,测试由编写测试代码的开发人员编写,维护和理解。 很有可能根本没有其他人需要阅读测试,这很好。

在BDD设置中,不仅需要开发人员编写功能,还需要对测试有更多的了解。 还有更多的利益相关者对产品的行为表现出兴趣。
这些人员可能包括质量检查人员,产品分析师,销售人员,甚至是高层管理人员。

这意味着,在理想情况下,需要以某种方式编写BDD测试,以便任何了解该产品的人都可以选择并理解它们。

两者之间的区别在于:

const assert = require('assert');
const webdriver = require('selenium-webdriver');
const browser = new webdriver.Builder()
  .usingServer()
  .withCapabilities({'browserName': 'chrome' })
  .build();

browser.get('http://en.wikipedia.org/wiki/Wiki');
browser.findElements(webdriver.By.css('[href^="/wiki/"]'))
.then(function(links){
  assert.equal(19, links.length); // Made up number
  browser.quit();
});

和:

Given I have opened a Web Browser
When I load the Wikipedia article on "Wiki"
Then I have "19" Wiki Links

两项测试的功能完全相同,但实际上是一种人类可读的方法,而另一种方法只有熟悉JavaScript和Selenium的人才能阅读。

本文将向您展示如何使用Cucumber.js框架在JavaScript项目中实现BDD测试,从而使您可以从产品的这种测试级别中受益。

什么是黄瓜/小黄瓜?

Cucumber是用于行为驱动开发的测试框架 。 它的工作原理是允许您以Gherkin形式定义测试,并通过将它们绑定到代码来使这些Gherkins可执行。

Gherkin是用于编写Cucumber测试的领域特定语言(DSL)。 它允许以人类可读的格式编写测试脚本,然后可以在产品开发中的所有涉众之间共享该脚本。

Gherkin文件是包含以Gherkin语言编写的测试的文件。 这些文件通常具有.feature文件扩展名。 这些Gherkin文件的内容通常简称为“ gherkins”。

小黄瓜

在Gherkin定义的测试中,您具有功能场景的概念。 这些类似于其他测试框架中的测试套件和测试用例,允许使用干净的方法来构建测试。

一个方案实际上只是一个测试。 它应该测试您的应用程序中的一件事。

功能是一组相关方案。 这样,它将测试应用程序中的许多相关内容。 理想情况下,Gherkin文件中的功能将紧密地映射到应用程序中的功能-即名称。

每个Gherkin文件仅包含一个功能,每个功能包含一个或多个场景。

然后,场景由步骤组成,这些步骤以特定的方式排序:

  • 给定–这些步骤用于设置测试之前的初始状态
  • 什么时候–这些步骤是要执行的实际测试
  • 然后–这些步骤用于确定测试结果

理想情况下,每种情况都应该是一个测试用例,因此“何时”步骤的数量应保持很少。

步骤完全是可选的。 例如,如果根本不需要进行任何设置,则可能没有“给定”步骤。

Gherkin文件旨在使人类可以阅读,并且可以使参与产品开发的任何人受益。 这包括非技术人员,因此Gherkin文件应始终以商务语言而非技术语言编写。 例如,这意味着您没有引用单个UI组件,而是描述了您要测试的产品概念。

小黄瓜测试示例

以下是用于在Google中搜索Cucumber.js的小黄瓜示例

Given I have loaded Google
When I search for "cucumber.js"
Then the first result is "GitHub - cucumber/cucumber-js: Cucumber for JavaScript"

立刻,我们可以看到该测试告诉我们该怎么做而不是该怎么做。 它是用一种对任何阅读它的人都有意义的语言编写的,而且重要的是,无论如何对最终产品进行调整,它都很可能是正确的。 Google可以决定完全更改其UI,但只要功能相同,小黄瓜仍然是准确的。

您可以在Cucumber Wiki上了解有关“ Given when While ”的更多信息。

Cucumber.js

一旦以Gherkin形式编写了测试用例,就需要某种方式来执行它们。 在JavaScript世界中,有一个名为Cucumber.js的模块 ,您可以执行此操作。 通过允许您定义JavaScript代码来工作,该代码可以连接到Gherkin文件内部定义的各个步骤。 然后,它通过加载Gherkin文件并以正确的顺序执行与每个步骤关联的JavaScript代码来运行测试。

例如,在上面的示例中,您将执行以下步骤:

Given('I have loaded Google', function() {});
When('I search for {stringInDoubleQuotes}', function() {});
Then('the first result is {stringInDoubleQuotes}', function() {});

不必太担心所有这些含义–稍后将详细解释。 不过从本质上讲,它定义了Cucumber.js框架将代码与Gherkin文件中的步骤相关联的某些方式。

在构建中包含Cucumber.js

包括Cucumber.js在您的构建很简单,只要加入cucumber模块到您的构建,然后将其配置为运行。 第一部分很简单:

$ npm install --save-dev cucumber

其中的第二个取决于您如何执行构建。

手动运行

手动执行Cucumber相对容易,并且确保您可以首先执行此操作是一个好主意,因为以下解决方案都是自动化的方法。

安装后,可执行文件将为./node_modules/.bin/cucumber.js 。 运行它时,它需要知道在文件系统上可以找到其所有必需文件的位置。 这些都是Gherkin文件和要执行的JavaScript代码。

按照惯例,所有Gherkin文件都将保存在features目录中,如果您没有其他说明,则Cucumber会在同一目录中查找JavaScript代码以执行。 指示它在哪里寻找这些文件是明智的做法,以便您可以更好地控制构建过程。

例如,如果将所有Gherkin文件保留在myFeatures目录中,并将所有JavaScript代码myStepsmySteps则可以执行以下操作:

$ ./node_modules/.bin/cucumber.js ./myFeatures -r ./mySteps

-r标志是一个目录,其中包含自动进行测试所需的JavaScript文件。 还有其他一些标记可能也很有趣-只需阅读帮助文本即可了解它们的工作原理: $ ./node_modules/.bin/cucumber.js --help

这些目录是递归扫描的,因此您可以根据自己的具体情况尽可能浅或深地嵌套文件。

npm脚本

手动运行Cucumber后,将其作为npm脚本添加到构建中是很简单的情况。 您只需要向package.json中添加以下命令(不包含完全限定的路径,因为npm会为您处理),如下所示:

"scripts": {
  "cucumber": "cucumber.js ./myFeatures -r ./mySteps"
}

完成后,您可以执行:

$ npm run cucumber

并且它将完全像以前一样执行Cucumber测试。

咕unt声

确实存在用于执行Cucumber.js测试的Grunt插件。 不幸的是,它已经过时,并且不能与Cucumber.js的最新版本一起使用,这意味着如果您使用它,将会错过很多改进。

相反,我的首选方法是简单地使用grunt-shell插件以与上述完全相同的方式执行命令。

安装完成后,只需将以下插件配置添加到您的Gruntfile.js配置:

shell: {
  cucumber: {
    command: 'cucumber.js ./myFeatures -r ./mySteps'
  }
}

现在,像以前一样,您可以通过运行grunt shell:cucumber来执行测试。

古尔普

Gulp与Grunt处于完全相同的情况,因为现有的插件已经过时,将使用旧版本的Cucumber工具。 同样,在这里您可以像其他情况下一样,使用gulp-shell模块执行Cucumber.js命令。

设置很简单:

gulp.task('cucumber', shell.task([
  'cucumber.js ./myFeatures -r ./mySteps'
]));

现在,像以前一样,您可以通过运行gulp cucumber来执行测试。

您的第一个黄瓜测试

请注意,本文中的所有代码示例均可在GitHub上获得

现在我们知道了如何执行Cucumber,让我们实际编写一个测试。 在这个例子中,我们将做一些相当人为的事,只是为了展示系统的运行情况。 实际上,您需要做更多的事情,例如直接调用您正在测试的代码,对正在运行的服务进行HTTP API调用或控制Selenium来驱动Web浏览器来测试您的应用程序。

我们的简单示例将证明数学仍然有效。 我们将具有两个功能-加法和乘法。

首先,让我们开始设置。

$ npm init
$ npm install --save-dev cucumber
$ mkdir features steps

如何执行测试完全取决于您。 对于此示例,为简单起见,我将手动进行此操作。 在真实的项目中,您可以使用上述选项之一将其集成到构建中。

$ ./node_modules/.bin/cucumber.js features/ -r steps/
0 scenarios
0 steps
0m00.000s
$

现在,让我们编写第一个实际功能。 这将进入features/addition.feature

Feature: Addition
  Scenario: 1 + 0
    Given I start with 1
    When I add 0
    Then I end up with 1

  Scenario: 1 + 1
    Given I start with 1
    When I add 1
    Then I end up with 2

很简单,很容易阅读。 准确地告诉我们我们在做什么,而没有告诉我们我们如何做。 让我们尝试一下:

$ ./node_modules/.bin/cucumber.js features/ -r steps/
Feature: Addition

  Scenario: 1 + 0
  ? Given I start with 1
  ? When I add 0
  ? Then I end up with 1

  Scenario: 1 + 1
  ? Given I start with 1
  ? When I add 1
  ? Then I end up with 2

Warnings:

1) Scenario: 1 + 0 - features/addition.feature:3
   Step: Given I start with 1 - features/addition.feature:4
   Message:
     Undefined. Implement with the following snippet:

       Given('I start with {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

2) Scenario: 1 + 0 - features/addition.feature:3
   Step: When I add 0 - features/addition.feature:5
   Message:
     Undefined. Implement with the following snippet:

       When('I add {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

3) Scenario: 1 + 0 - features/addition.feature:3
   Step: Then I end up with 1 - features/addition.feature:6
   Message:
     Undefined. Implement with the following snippet:

       Then('I end up with {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

4) Scenario: 1 + 1 - features/addition.feature:8
   Step: Given I start with 1 - features/addition.feature:9
   Message:
     Undefined. Implement with the following snippet:

       Given('I start with {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

5) Scenario: 1 + 1 - features/addition.feature:8
   Step: When I add 1 - features/addition.feature:10
   Message:
     Undefined. Implement with the following snippet:

       When('I add {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

6) Scenario: 1 + 1 - features/addition.feature:8
   Step: Then I end up with 2 - features/addition.feature:11
   Message:
     Undefined. Implement with the following snippet:

       Then('I end up with {int}', function (int, callback) {
         // Write code here that turns the phrase above into concrete actions
         callback(null, 'pending');
       });

2 scenarios (2 undefined)
6 steps (6 undefined)
0m00.000s
$

哇。 我们刚刚编写了《小黄瓜》,一切都执行了。 这是行不通的,因为我们尚不知道如何执行这些步骤,但是Cucumber清楚地告诉了我们这一点。

然后让我们编写第一步文件。 这将按照Cucumber输出告诉我们的方式简单地执行步骤,该步骤没有做任何有用的事情,但会整理输出。

这在steps/maths.js

const defineSupportCode = require('cucumber').defineSupportCode;

defineSupportCode(function({ Given, Then, When }) {
  Given('I start with {int}', function (int, callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });
  When('I add {int}', function (int, callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });
  Then('I end up with {int}', function (int, callback) {
    // Write code here that turns the phrase above into concrete actions
    callback(null, 'pending');
  });
});

defineSupportCode钩子是Cucumber.js的一种允许您提供将用于各种不同情况的代码的方法。 这些都将被涵盖,但是实际上,任何时候只要您想编写Cucumber将直接调用的代码,那么它都必须位于这些块之一中。

您会注意到,这里的示例代码定义了三个不同的步骤–每个步骤分别针对“给定”,“何时”和“之后”。 这些块中的每个块都有一个字符串(如果需要,可以使用正则表达式),该字符串与功能文件中的某个步骤匹配,并且还包含一个与该步骤匹配的函数。 可以将占位符放入步骤字符串中(或者,如果您使用的是Regex,则可以使用捕获表达式),并且这些占位符将被提取出来并用作函数的参数。

执行此操作将给出更简洁的输出,而实际上仍然没有做任何事情:

$ ./node_modules/.bin/cucumber.js features/ -r steps/
Feature: Addition

  Scenario: 1 + 0
  ? Given I start with 1
  - When I add 0
  - Then I end up with 1

  Scenario: 1 + 1
  ? Given I start with 1
  - When I add 1
  - Then I end up with 2

Warnings:

1) Scenario: 1 + 0 - features/addition.feature:3
   Step: Given I start with 1 - features/addition.feature:4
   Step Definition: steps/maths.js:4
   Message:
     Pending

2) Scenario: 1 + 1 - features/addition.feature:8
   Step: Given I start with 1 - features/addition.feature:9
   Step Definition: steps/maths.js:4
   Message:
     Pending

2 scenarios (2 pending)
6 steps (2 pending, 4 skipped)
0m00.002s

现在,使它一切正常。 我们要做的就是在步骤定义中实现代码。 我们还将整理一些内容,以使内容更易于阅读。 从本质上讲,由于我们不执行任何异步操作,因此无需使用callback参数。

之后,我们的“ steps / maths.js”将如下所示:

const defineSupportCode = require('cucumber').defineSupportCode;
const assert = require('assert');

defineSupportCode(function({ Given, Then, When }) {
  let answer = 0;

  Given('I start with {int}', function (input) {
    answer = input;
  });
  When('I add {int}', function (input) {
    answer = answer + input;
  });
  Then('I end up with {int}', function (input) {
    assert.equal(answer, input);
  });
});

执行它看起来像这样:

$ ./node_modules/.bin/cucumber.js features/ -r steps/
Feature: Addition

  Scenario: 1 + 0
  ✔ Given I start with 1
  ✔ When I add 0
  ✔ Then I end up with 1

  Scenario: 1 + 1
  ✔ Given I start with 1
  ✔ When I add 1
  ✔ Then I end up with 2

2 scenarios (2 passed)
6 steps (6 passed)
0m00.001s

一切都过去了。 现在我们知道加法工作正常。

请注意,我们只需要编写少量代码,而Cucumber系统会将它们粘合在一起。
通过简单地指定如何从Gherkin文件执行步骤代码,我们获得了自动参数化测试。 这意味着添加更多方案非常容易。

接下来,让我们证明乘法同样有效。 为此,我们将在features/multiplication.feature编写以下Gherkin:

Feature: Multiplication

  Scenario: 1 * 0
    Given I start with 1
    When I multiply by 0
    Then I end up with 0

  Scenario: 1 * 1
    Given I start with 1
    When I multiply by 1
    Then I end up with 1

  Scenario: 2 + 2
    Given I start with 2
    When I multiply by 2
    Then I end up with 4

然后,在我们的steps/maths.js实现新的步骤。 为此,我们只需要在defineSupportCode方法内部添加以下块:

When('I multiply by {int}', function (input) {
  answer = answer * input;
});

而已。 运行此命令将得到以下结果:

$ ./node_modules/.bin/cucumber.js features/ -r steps/
Feature: Addition

  Scenario: 1 + 0
  ✔ Given I start with 1
  ✔ When I add 0
  ✔ Then I end up with 1

  Scenario: 1 + 1
  ✔ Given I start with 1
  ✔ When I add 1
  ✔ Then I end up with 2

Feature: Multiplication

  Scenario: 1 * 0
  ✔ Given I start with 1
  ✔ When I multiply by 0
  ✔ Then I end up with 0

  Scenario: 1 * 1
  ✔ Given I start with 1
  ✔ When I multiply by 1
  ✔ Then I end up with 1

  Scenario: 2 + 2
  ✔ Given I start with 2
  ✔ When I multiply by 2
  ✔ Then I end up with 4

5 scenarios (5 passed)
15 steps (15 passed)
0m00.003s
$

如此简单,我们就有了一个非常容易扩展的测试套件,证明了数学的有效性。 作为练习,为什么不尝试扩展它以支持减法呢? 如果您遇到困难,可以在评论中寻求帮助。

更多高级Cucumber.js技巧

这一切都非常好,但是Cucumber可以做很多更高级的事情,这将使我们的生活更轻松。

异步步骤定义

到目前为止,我们只编写了同步步骤定义。 在JavaScript世界中,这通常不够好。 JavaScript中的太多内容需要异步进行,因此我们需要某种方式来处理它。

值得庆幸的是,Cucumber.js具有两种内置的处理方式,具体取决于您的偏好。

上面暗示的方法(这是处理异步步骤的更传统的JavaScript方法)是使用回调函数。 如果您指定步骤定义应将回调函数作为其最后一个参数,则直到触发此回调后,才认为该步骤已完成。 在这种情况下,如果使用任何参数触发了回调,则将其视为错误,并且该步骤将失败。 如果在没有任何参数的情况下触发了该步骤,则认为该步骤已成功。 但是,如果根本不触发回调,则框架最终将超时并且无论如何都将失败。 这个故事的主旨? 如果您接受回调参数,请确保调用它。

例如,使用回调进行HTTP API调用的步骤定义可能如下所示。 这是使用Request编写的,因为它在响应时使用了回调。

When('I make an API call using callbacks', function(callbacks) {
  request('http://localhost:3000/api/endpoint', (err, response, body) => {
    if (err) {
      callback(err);
    } else {
      doSomethingWithResponse(body);
      callback();
    }
  });
});

替代的首选方法是通过返回类型。 如果您从自己的步骤中返回了一个承诺,那么只有在解决该承诺时,该步骤才被视为已完成。 如果Promise被拒绝,则该步骤将失败;如果Promise被实现,则该步骤将成​​功。

但是,或者,如果您返回的内容不是Promise,则该步骤将立即被视为成功。 这包括返回undefinednull 。 这意味着您可以在执行步骤期间选择是否需要返回Promise,并且框架将根据需要进行调整。

例如,使用Promises进行HTTP API调用的步骤定义可能如下所示。 这是使用Fetch API编写的,因为它会在响应时返回Promise。

When('I make an API call using promises', function() {
  return fetch('http://localhost:3000/api/endpoint')
    .then(res => res.json())
    .then(body => doSomethingWithResponse(body));
});

功能背景

功能背景是一个Gherkin代码段,该代码段位于文件中每个方案的开头。 这使得可以在每个场景之间轻松共享通用的设置步骤,而无需重复它们。

通过使用Background关键字而不是Scenario关键字来编写Background 。 理想情况下,应该只包括“给定”步骤,因为包括每个测试之间共享的“何时”或“然后”步骤是没有意义的。 但是,该框架不会限制您,因此在构造测试结构时要小心。

使用此功能,我们可以按如下所示重写我们的添加功能:

Feature: Addition

  Background:
    Given I start with 1

  Scenario: 1 + 0
    When I add 0
    Then I end up with 1

  Scenario: 1 + 1
    When I add 1
    Then I end up with 2

这实际上与以前完全相同,但是由于我们已经排除了常见的设置步骤,因此它略短一些。

方案大纲

方案大纲是从测试数据表生成方案的一种方法。 由于我们可以重复多次使用完全相同的测试脚本,并插入不同的值,因此可以以比以前更有效的方式进行参数化测试。

通过使用Scenario Outline关键字而不是Scenario关键字编写Scenario Outline ,然后提供一个或多个“ Examples表。 然后,将“ Examples表中的参数替换为“ Scenario Outline以生成要运行的方案。

使用此方法,我们可以按以下方式重新编写乘法功能:

Feature: Multiplication

  Scenario Outline: <a> * <b>
    Given I start with <a>
    When I multiply by <b>
    Then I end up with <answer>

  Examples:
    | a | b | answer |
    | 1 | 0 | 0      |
    | 1 | 1 | 1      |
    | 2 | 2 | 4      |

同样,这与以前完全相同,但是重复次数明显减少。 您实际上会看到是否运行此命令,它会在输出中生成与之前完全相同的方案:

Feature: Multiplication

  Scenario: 1 * 0
  ✔ Given I start with 1
  ✔ When I multiply by 0
  ✔ Then I end up with 0

  Scenario: 1 * 1
  ✔ Given I start with 1
  ✔ When I multiply by 1
  ✔ Then I end up with 1

  Scenario: 2 * 2
  ✔ Given I start with 2
  ✔ When I multiply by 2
  ✔ Then I end up with 4

数据表

我们刚刚看到了方案概要中使用的表格,用于生成可以从中生成方案的数据。
但是,我们也可以在方案内部使用数据表。 这些可以用作提供数据表,结构化输入或许多其他东西的方式。

例如,可以将加法业务情景重写为添加任意数量的值,如下所示:

Scenario: Add numbers
  Given I start with 0
  When I add the following numbers:
    | 1 |
    | 2 |
    | 3 |
    | 4 |
  Then I end up with 10

对于这个简单的示例,该步骤将如下所示:

When('I add the following numbers:', function (table) {
  answer = table.raw()
    .map(row => row[0])
    .map(v => parseInt(v))
    .reduce((current, next) => current + next, answer);
});

我们提供的table参数是一个DataTable对象,该对象上具有可以调用的raw方法。 此方法返回数据表中所有值的2D数组,以便外部数组中的每个条目都是表中的一行,内部数组中的每个条目都是该行中的一个单元格(作为字符串)。

一个更复杂的示例可能是使用数据表填充表单。 然后,可以使用该表来提供所有输入,而不是很难理解步骤定义。 这可能是这样的:

Scenario: Create a new user
  When I create a new user with details:
    | Username | graham               |
    | Email    | my.email@example.com |
    | Password | mySecretPassword     |
  Then the user is created successfully

在这种情况下,数据表类可以使我们通过使用rowsHash方法更轻松地访问表。

我们的步骤可能如下所示:

When('I create a new user with details:', function (table) {
  const data = table.rowsHash();
  createUser(data);
});

在这种情况下, data对象将从数据表中解析出来,看起来像:

{
  "Username": "graham",
  "Email": "my.email@example.com",
  "Password": "mySecretPassword"
}

第一列中的键使访问字段变得非常容易。

钩子

与大多数测试框架一样,Cucumber.js 支持在场景运行之前和之后执行的挂钩

这些设置以与步骤定义相同的方式进行设置,并且按照名称描述的方式简单地调用-在方案运行之前或之后,无论成功或失败。

作为一个简单的示例,要使我们的数学功能更可靠,我们可以执行以下操作:

defineSupportCode(function({ Before, Given, Then, When }) {
  let answer;

  Before(function() {
    answer = 0;
  });
});

如上增强我们的数学步骤文件,将确保在每种情况下运行之前将answer变量重置为0,这意味着如果从0开始,则不需要给定步骤。

如果需要,这些挂钩的第一个参数始终是挂钩之前或之后运行的方案的方案结果。 这可用于使功能适应正在运行的方案。

通过将回调函数作为第二个参数或返回Promise,可以使钩子与步骤定义完全相同,从而使其成为异步的。

大事记

如果无论出于何种原因,简单的前后钩子都不足以满足您的需求,那么还有更多事件需要探索 。 这些使我们能够处理:

  • BeforeFeatures –在所有功能运行之前调用一次,并提供了功能列表。
  • BeforeFeature –在功能随附的每个功能文件运行之前调用。
  • BeforeScenario –在每个场景运行之前调用,随场景一起提供。 这大致类似于“ Before”挂钩。
  • BeforeStep –在每个步骤运行之前调用,随步骤一起提供。
  • StepResult –在每个步骤运行后调用,并提供步骤的结果。
  • AfterStep –在每个步骤运行后调用,并随该步骤一起提供。
  • ScenarioResult –在每个方案运行之后调用,并提供方案的结果。
  • AfterScenario –在每个方案运行后调用,由该方案提供。 这大致类似于“ After”钩子。
  • AfterFeature –在每个功能运行后调用,由功能提供。
  • FeaturesResult –一切运行后调用一次,并提供一切运行的结果。
  • AfterFeatures –一切运行后调用一次,并提供功能列表。

它们与测试框架的整个生命周期具有完全的交互作用,将按上面列出的顺序进行调用。

使用defineSupportCode方法中的registerHandler方法来处理这些事件。 可能看起来像这样:

defineSupportCode(function({ registerHandler }) {
  registerHandler('BeforeStep', function(step) {
    console.log('About to execute step:' + util.inspect(step));
  });
  registerHandler('ScenarioResult', function(scenario) {
    console.log('Result of Scenario:' + util.inspect(scenario));
  });
});

通过将回调函数作为第二个参数,或者通过返回Promise,可以使事件处理程序与步骤定义完全相同,从而使其异步。

世界–共享代码和数据

到目前为止,我们还无法在步骤之间共享代码。 我们可以很容易地拥有任意数量的JavaScript文件,其中包含所需的步骤定义,挂钩,事件等,但是它们彼此独立(不计算节点模块系统存储状态的技巧)。

碰巧,这是不正确的。 Cucumber.js有一个“世界”的概念,它是场景运行的所有状态。 无论定义了步骤定义的文件是什么,所有步骤定义,钩子和事件处理程序都可以通过访问this参数来访问它。这就是为什么所有示例都使用传统的function关键字而不是arrow函数编写的原因。 JavaScript中的Arrow函数为您重新绑定了this变量,这意味着您无法访问测试中可能需要的World状态。

这按原样工作,不需要额外的处理,因此您可以立即使用它。 立刻,这意味着我们可以通过在多个文件之间逻辑上拆分Cucumber代码,使我们的代码更加整洁,并使它们都能按预期工作,同时仍然可以访问共享状态。

摘要

行为驱动开发是确保您的产品具有正确行为的一种奇妙的方式,而Cucumber作为工具是一种非常有效的实现方式,以使产品中的每个利益相关者都可以阅读,理解甚至编写行为测试。

本文只是介绍Cucumber的功能,因此,我鼓励您自己尝试一下以了解其功能。 黄瓜也有一个非常活跃的社区,如果需要,他们的邮件列表Gitter频道是寻求帮助的好方法。

您已经在使用Cucumber吗? 这篇文章是否鼓励您尝试一下? 无论哪种方式,我都希望在下面的评论中收到您的来信。

本文由Jani Hartikainen进行同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!

From: https://www.sitepoint.com/bdd-javascript-cucumber-gherkin/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值