BDD Testing with Cucumber
Tags: javascript, testing
Published: 2020-11-28 00:21:26

Recently at work I got the opportunity to write Behavior Driven Development tests using [Cucumber.js](https://cucumber.io/docs/installation/javascript/). Cucumber uses a language called Gherkin to write the test steps and uses javascript to execute them. Gherkin is written in plain english [(or a selection of other languages)](https://cucumber.io/docs/gherkin/languages/) and is made to be read by any (particularly non-dev) team members. If you haven't written tests before Cucumber is a great place to start.

## Setting up the WebDriver

```javascript
// ~/cuc-test/bdd/features/step_definitions/stepdefs.js
const { Builder, Capabilities, By } = require('selenium-webdriver')

const driver = new Builder().withCapabilities(Capabilities.chrome()).build()
```

[Selenium WebDriver](https://www.selenium.dev/documentation/en/webdriver/) is the technology we'll use to run our tests. At a high level, it follows the test steps and executes them in a browser. `Builder` creates the driver when we run our tests. `Capabilities` is how you declare your browser. `By` is a keyword we'll use in our tests to define what we're looking for.

*I found the documentation for Selenium a bit hard to navigate. I mostly referenced the [API documentation for Javascript](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index.html) or just searched the issue I was having.*

## Gherkin keywords

![Screen shot of the initial test page](https://dev-to-uploads.s3.amazonaws.com/i/x0n9xd9dakv85k0a3xfl.png)

I spun up a simple html page with a few headers, a text input, a drop down menu, and a button to submit your changes. The first test I wrote checks that the default values are all as I'd expect them to be.

```gherkin
Scenario: I want to check default values of the page
    Given I am on the home page
    Then I should see the default title
    And I should see the text input field
```

**Scenario, Given, Then & And** are all keywords that come from the gherkin syntax. In my `Scenario` statement I summarize what the following test steps do. This keyword is used to group steps together and give context for users running the tests.

`Given, Then & And` declare steps for a test. The string following these declaration keywords connect the step and code to execute. If you have a typo or don't match them exactly you'll receive an `Undefined` error when you run the tests.  This is the meat and potatoes of Cucumber testing.

![Sasha with Meat and Potatoes GIF](https://media.giphy.com/media/I0WQIk0xbBVvO/source.gif)

```javascript
Given('I am on the home page', function () {
    driver.get('localhost:3000/')
})

Then('I should see the default title', function () {
    driver.findElement(By.xpath(`.//*[text()[contains(.,'Welcome to the site.')]]`))
})

Then('I should see the text input field', function () {
    driver.findElement(By.id('textbox'))
})
```

The first step of my scenario navigates to the the page I pass into the `get` method. The second step tells the webdriver to search the page for the text I pass into the `xpath`. The third searches for any element with an ID of textbox. *Remember, we imported `By` from Selenium in our set up. [There a plenty of options to search by.](https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/index_exports_By.html)*

```gherkin
Scenario: I want to make sure all the colors work
        Given I am on the home page
        Then I want to select the color 'Green' from the drop down
        And I should submit the changes
        Then I should make sure the third header is 'Green'
```

Since I'm checking the default values in the last scenario I hardcode the parameters to search for. But it's common to pass parameters through the test steps. Notice how green is in quotations in steps two and four. 

```javascript
Then('I want to select the color {string} from the drop down', function (string) {
    const dropDown = driver.findElement(By.id('selector'))
    dropDown.click()
    dropDown.sendKeys(string)
    dropDown.click()
})

Then('I should make sure the third header is {string}', async function (string) {
    const color = await driver.findElement(By.id('changing-header')).getCssValue('color')
    // getCSSValue returns an rgb value
    // colorMap is an object with keys of string and values of the associated value
    expect(color).to.equal(colorMap[string])
})
```

`Then` is a function that takes two parameters. The first parameter is the string that we used to declare the test step. Inside of that string we signal a nested variable by wrapping it in curly braces, `{string}`. 

The second parameter is a callback function, where we pass in the variable. *Cucumber is particular about what you call the variable, if you pass in a number character you'd use `int`. If you have more than one variable you'd declare it as `string, string2, string3` in the callback but just as `{string}` in the first parameter.* 

I also used a couple other methods of the driver like `click()`, `sendKeys(string)`, and `getCssValue`. Those methods do exactly what the name implies. `sendKeys` sends the string you pass in to the value field.

___

While working on writing a bunch of tests for old components I began to think of the tests I would write as I created new components. What types of identifiers does this component need to be testable? Is it a class name, an ID, or certain unique text that won't appear elsewhere when this is loaded? I began to think about how I would write my new components to comply with the tests I'd eventually write. In some cases, I started with the tests first. Cucumber encourages that by giving giving you the line of code you need to write when it encounters a step it doesn't recognize.

![Undefined error thrown by Cucumber](https://dev-to-uploads.s3.amazonaws.com/i/hyf8nuqv95kc1blsuc6l.png)

You can [fork this repo](https://github.com/javila35/cucumber-example) and open it up on your machine if you'd like to learn by playing instead. There's a couple of tests that need to fixing in order to pass. Feel free to submit a PR if you come up with any cool tests.

*Cover image: ["cucumbers en route to pickledom" by Stacy Spensley is licensed with CC BY 2.0.](https://creativecommons.org/licenses/by/2.0/)*