Home > Web Front-end > JS Tutorial > BDD in JavaScript: Getting Started with Cucumber and Gherkin

BDD in JavaScript: Getting Started with Cucumber and Gherkin

Lisa Kudrow
Release: 2025-02-16 13:09:11
Original
1007 people have browsed it

BDD in JavaScript: Getting Started with Cucumber and Gherkin

BDD in JavaScript: Getting Started with Cucumber and Gherkin

The benefits of test-driven development (TDD) are well known, which improve product quality and development efficiency. Every time you write a code test, you can ensure the correctness of the code and promptly detect possible future code errors.

Behavior-driven development (BDD) goes a step further on this, testing the product's behavior rather than just code to ensure that the product behaves in line with expectations. This article will describe how to write BDD-style automated acceptance tests using the Cucumber framework. The advantage of Cucumber is that test cases can be written in concise natural language for easy understanding by non-technical personnel in the project. After reading this article, you can tell if Cucumber is right for your team and start writing your own acceptance test. Ready? Let's get started!

Key Points

BDD Based on TDD, it is tested for the behavior of the product rather than the code, making it easier to be understood by a wider range of stakeholders, including non-technical personnel.
  • Cucumber is a BDD framework that uses Gherkin, an easy-to-understand language to define test cases, ensuring that all stakeholders can understand and participate in the testing process.
  • Gherkin syntax structures tests into scenarios and features, using simple Given, When, Then steps to describe behavior without specifying technical implementations.
  • Cucumber.js integrates with JavaScript projects, runs Gherkin-defined tests, and supports asynchronous operations and external testing tools through various plug-ins and configurations.
  • Cucumber.js settings include installing the module via npm, configuring it to find feature files and step definitions, and optionally integrating it into a build script or task runner such as Grunt or Gulp.
  • This article provides a basic Cucumber test example that demonstrates setting up a Gherkin scenario for addition and multiplication, using simple assertions to verify the correctness of these operations.
  • This article also outlines the advanced features of Cucumber.js, such as support for asynchronous testing, scenario outlines for parameterized testing, and hooks for setting preconditions and postconditions to enhance testing capabilities.
The difference between BDD and TDD

It is mainly reflected in the structure and writing method of the test. In TDD, tests are written, maintained, and understood by developers writing code. Others may not need reading tests at all, and that's totally OK. But in BDD, testing needs to be understood by far more people than developers who write functions. Many stakeholders care about whether the product behaves correctly, such as QA personnel, product analysts, sales personnel, and even senior management. This means that, ideally, BDD testing needs to be written in a way that anyone who understands the product can understand. The difference is:

and:
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); // 假设的数字
  browser.quit();
});
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login
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); // 假设的数字
  browser.quit();
});
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

These two tests do the same, but one is readable natural language and the other is only understood by people who know JavaScript and Selenium. This article will show you how to implement BDD testing in a JavaScript project using the Cucumber.js framework, so that your product can benefit from this level of testing.

What is Cucumber/Gherkin?

Cucumber is a testing framework for behavior-driven development. It allows you to define tests in Gherkin format and make these Gherkins executable by binding them to code. Gherkin is a domain-specific language (DSL) used to write Cucumber tests. It allows the writing of test scripts in a human-readable format that can then be shared among all stakeholders in product development. A Gherkin file is a file containing tests written in the Gherkin language. These files usually have a .feature file extension. The contents of these Gherkin files are usually referred to as "Gherkin".

Gherkin

In Gherkin defined tests, you have the concepts of features and scenarios. They are similar to test suites and test cases in other test frameworks, providing a clear way to structure the test. The scenario is just a separate test. It should test only one aspect of the application. Features are a set of related scenarios. Therefore, it will test many related aspects of the application. Ideally, features in Gherkin files will be closely mapped with features in the application—hence the name. Each Gherkin file contains a feature, each feature contains one or more scenarios. The scene is then composed of steps, which are arranged in a specific order:

    Given – These steps are used to set the initial state before the test is executed
  • When – These steps are the tests to be actually performed
  • Then – These steps are used to assert the test results
Ideally, each scenario should be a separate test case, so the number of When steps should be kept very small. The steps are completely optional. For example, if you don't need to set anything at all, there may be no Given step. Gherkin files are designed to be easy to read and benefit anyone involved in product development. This includes non-technical people, so Gherkin files should always be written in business language rather than technical language. This means, for example, you do not reference a single UI component, but describe the product concept you want to test.

Gherkin test example

The following is a Gherkin example search for Google's Cucumber.js:

Given I have opened a Web Browser
When I load the Wikipedia article on "Wiki"
Then I have "19" Wiki Links
Copy after login
Copy after login
Copy after login
Copy after login
We can see immediately that this test tells us what to do, not how to do it. It is written in a language that anyone can understand, and—importantly—it is most likely to remain correct no matter how the final product is tweaked. Google may decide to change its UI completely, but Gherkin is still accurate as long as the functionality is equivalent. You can read more about Given When Then on the Cucumber wiki.

Cucumber.js

After writing test cases in Gherkin format, you need a way to execute them. In the JavaScript world, there is a module called Cucumber.js that allows you to do this. It allows you to define JavaScript code, which Cucumber.js can connect to various steps defined in the Gherkin file. It then runs the tests by loading the Gherkin file and executing the JavaScript code associated with each step in the correct order. For example, in the example above, you will have the following steps:

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); // 假设的数字
  browser.quit();
});
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Don't worry too much about all of these meanings--it will be explained in detail later. But essentially, it defines some methods that the Cucumber.js framework can use to bind your code to steps in the Gherkin file.

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

Include Cucumber.js in your build

Include Cucumber.js in your build, just add the cucumber module to your build and configure it to run. The first step is as follows:

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

The second step depends on how you perform the build.

Run manually

It is relatively easy to execute Cucumber manually, and it is best to make sure you can do it first, as the following solutions are all ways to do the same automatically. After installation, the executable will be ./node_modules/.bin/cucumber.js. When running it, it needs to know where on the file system you can find all the required files. These files include both the Gherkin file and the JavaScript code to be executed. By convention, all Gherkin files will be saved in the features directory, and if you do not instruct it to perform other operations, Cucumber will also look for JavaScript code to be executed in the same directory. However, it is wise to instruct it to find the location of these files so that you can have more control over the build process. For example, if you save all Gherkin files in the myFeatures directory and all JavaScript code in mySteps, you can do the following:

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

-r flag is a directory containing JavaScript files that Cucumber will automatically load for testing. There are some other signs that might be interesting too – just read the help text to understand how they work: $ ./node_modules/.bin/cucumber.js --help. These directories scan recursively, so you can nest files shallow or deep depending on the situation.

npm script

After running Cucumber manually, adding it to the build as an npm script is a simple case. You just add the following command (no fully qualified paths, as npm will handle them for you) to your package.json as follows:

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

After you are finished, you can execute:

$ npm install --save-dev cucumber
Copy after login
Copy after login

It will perform Cucumber tests exactly as you did before.

Grunt

A Grunt plugin does exist for performing Cucumber.js tests. Unfortunately, it's outdated and doesn't work with newer versions of Cucumber.js, which means you'll miss a lot of improvements if you use it. Instead, the way I prefer is simply to use the grunt-shell plugin to execute the command in exactly the same way as above. After installation, configure it simply add the following plugin configuration to your Gruntfile.js:

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); // 假设的数字
  browser.quit();
});
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

Now, as before, you can perform tests by running grunt shell:cucumber .

Gulp

Gulp is exactly the same as Grunt, because existing plugins are outdated and will use older versions of the Cucumber tool. Again, here you can use the gulp-shell module to execute the Cucumber.js command just like in other scenarios. Setting it up is simple:

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

Now, as before, you can perform tests by running gulp cucumber .

Your first Cucumber test

Please note that all code examples in this article can be found on GitHub.

Now that we know how to execute Cucumber, let's actually write a test. In this example, we will do something quite artificial just to show how the system works. In fact, you do more complex things, such as calling directly the code you are testing, making HTTP API calls to running services, or controlling Selenium to drive a web browser to test your application. Our simple example will prove that math is still valid. We will have two characteristics - addition and multiplication. First, let's set it up.

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

How you perform the test is entirely up to you. In this example, I will do it manually for simplicity. In a real project, you will integrate it into your build using one of the above options.

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

Now, let's write our first actual feature. This will be placed in features/addition.feature:

$ npm install --save-dev cucumber
Copy after login
Copy after login

Very simple and very easy to read. It tells us exactly what we are doing, without telling us how to do it. Let's try it:

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

Then let's write our first step file. This will simply implement the steps as the Cucumber output tells us, which will not do anything useful, but will sort out the output. This will be placed in steps/maths.js:

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); // 假设的数字
  browser.quit();
});
Copy after login
Copy after login
Copy after login
Copy after login
Copy after login

defineSupportCode Hook is a method of Cucumber.js that allows you to provide code that it will be used for a variety of different situations. All of this will be covered, but essentially, any time you want to write code that Cucumber will call directly, it needs to be inside one of these blocks. You will notice that the sample code here defines three different steps – one for each Given, When and Then. Each block gives a string (or a regular expression if you need it) that matches the steps in the attribute file, and the functions executed when that step matches. Placeholders can be placed in the step string (or use capture expression instead if you are using regular expressions), and these placeholders will be extracted and provided as arguments to your function. Doing this will provide a cleaner output while still doing nothing in reality:

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

Let's get it all working now. We just need to implement the code in our step definition. We will also do some sorting to make reading easier. This actually eliminates the need for callback parameters, because we are not doing any asynchronous operations. After that, our steps/maths.js will look like this:

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

Execute it looks like this:

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

That's it, we got a very easy to expand test suite that proves that math is correct. As an exercise, why not try to extend it to support subtraction? If you have trouble, you can ask for help in the comments.

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms, and some chapters are merged and simplified)

More advanced Cucumber.js tips

It's all good, but Cucumber can do something more advanced that will make our lives easier.

Async step definition

So far, we have only written the synchronization step definition. However, in the JavaScript world, this is usually not good enough. A lot of things in JavaScript need to be asynchronous, so we need some way to handle it. Thankfully, Cucumber.js has several built-in ways to deal with this, depending on your preference. The method suggested above, which is a more traditional JavaScript method that handles asynchronous steps, uses a callback function. If you specify that the step definition should have the callback function as its last parameter, the step is considered to be completed only after this callback is triggered. In this case, if the callback is triggered with any arguments, this is considered an error and the step will fail. If it is triggered without any parameters, the step is considered successful. However, if the callback is not triggered at all, the framework will eventually time out and fail the step. The meaning of the story? If you accept the callback parameter, make sure to call it. For example, the step definition of an HTTP API call using a callback might be as follows. This is written using Request because it uses callbacks on the response.

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

Another, and more preferred method is by returning type. If you return a Promise from a step, the step is considered completed only when the promise is completed. If the Promise is rejected, the step will fail; if the Promise is fulfilled, the step will succeed. Alternatively, if the content you returned is not a Promise, the step will be immediately considered successful. This includes returning undefined or null. This means that you can choose whether you need to return a Promise during the step execution and the framework will be adjusted as needed. For example, the step definition for making HTTP API calls using Promises might be as follows. This is written using the Fetch API because it returns a promise on the response.

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms, and some chapters are merged and simplified)

Feature background, scene outline, data table, hooks, events and world

These advanced features, such as feature background, scene outline, data table, as well as hook functions (Before, After, BeforeStep, AfterStep, AfterStep, etc.) and event processing mechanisms, can greatly improve testing efficiency and readability. By using these functions reasonably, it is possible to write more concise and easier to maintain BDD tests. World Objects allow sharing of data and state between different step definitions, simplifying testing logic.

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

Summary

Behavior-driven development is an excellent way to ensure that the product has the right behavior, and Cucumber, as a tool, is a very powerful way to achieve this so that every stakeholder of the product can read , understand and even write behavioral tests. This article just touches on the skin of what Cucumber can do, so I encourage you to try it out for yourself to understand its power. Cucumber also has a very active community, and their mailing list and Gitter channel are great ways to get help if you need it. Are you already using Cucumber? Does this article encourage you to try it? Either way, I want to hear you in the comments below. This article was peer-reviewed by Jani Hartikainen. Thanks to all SitePoint peer reviewers for getting SitePoint content to its best!

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

FAQs about BDD in JavaScript using Cucumber and Gherkin

(The following content is basically consistent with the original text, and it is slightly adjusted to maintain fluency and readability, and some sentences are replaced synonyms)

The above is the detailed content of BDD in JavaScript: Getting Started with Cucumber and Gherkin. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template