tonygo_
NUIT

Why you should use Gherkin, as a software developer?

The advantage to use Gherkin and an E2E approach for testing.

tony-gorez

Hi 👋 ! My name is Tony, Im a consultant software developer and open source maintainer. I put all the stuff that I'll learn here !

Gherkin - BDD - E2E - Test automation - 2020-09-22

Context

During the past three months, I mostly worked on porting an API from a framework to another. The service was a kind of legacy code: just documentation about the service and no tests.

So I mixed up adding a few tests and fixes. But without e2e testing, it was hard to predict failures and regressions. This migration finally takes a bit of time. When I step back now, I think that I could take a better approach.

Define the specification

If the specifications of your endpoints are not well defined. You'll start on one foot, especially with HTTP transport, where there is a lot to check (body, headers, cookies, status code). Moreover, a client could expect specific information. Imagine that your current framework automatically sets a response header (under the hood) but, the new one doesn't. It could break the client (front-end)!

It's why you need an e2e approach to be sure that the API responds as expected. More than an e2e testing approach, you need a source of truth: something that represents the service's interface for everyone.

Source of truth

User stories

If you're lucky to work with a product team, you could probably use their user stories/requirements.

Then, you will be able to write test cases that mimic (as much as you can) the behavior.

The cons of this approach are:

  • You'll make an effort to interpret specifications and then write the tests, and you'll probably miss something.
  • The source of truth is split between the original one (ticket) and the code (tests)

Gherkin

The solution should be something understandable by the product made and actionable by the tech team. This is where Gherkin shines.

Gherkin is a language that "[...] uses a set of special keywords to give structure and meaning to executable specifications [...]" (Cucumber documentation)

Imagine the following hello.feature file:

Feature: Hello
  Scenario: Scenario all good
    Given I have the api gateway
      And I have the path "/hello"
      And I have the method "GET"
    When I run the API
    Then I should receive a response with the status 200
      And the response body at "value" should equal "world"

Easy to ready, is not? Believe me! It's easy to write too. Gherkin helps you to describe the behavior of your API in an expressive way. So it's easy to add them in the spec.

Even if it's easy to write, Gherkin doesn't come with any structure or domains specific steps. Moreover, you have to run the tests by yourself. But don't be afraid. The open-source community put a lot of effort into the development of tools.

Today we'll use RestQA, which provides:

  • A Gherkin runner
  • Built-in steps for test API.
  • Dashboard for non-tech mates

Let's open an IDE.

Example

You'll find out all the code snippets in this repository.

You should first:

# create a directory
mkdir my-super-service
cd my-super-service

# init the repository
npm init -y

# instal dependencies
npm install express restqa

## init RestQA project
restqa init -y

Requirements

Let use this hello.feature file we had above.

Feature: Hello
  Scenario: Scenario all good
    Given I have the api gateway
      And I have the path "/hello"
      And I have the method "GET"
    When I run the API
    Then I should receive a response with the status 200
      And the response body at "value" should equal "world"

Build the app (first version)

Here the server code for the feature:

// express-server.js
const express = require("express");

const app = express();
const port = 9999;

app.get("/hello", (_, res) => {
  res.send({ value: "world" });
});

app.listen(port);

Let's add two commands in the package.json:

{
  "scripts": {
      "start:express": "node express-server.js",
      "test": "restqa run . -x 'npm run start:express'"
  }
}

The start:express command starts the express server.

The test command runs the server, then RestQA thank the -x options (or --exec).

Then run npm run test:

➜  rqapi git:(express-version) npm run test

> rqapi@1.0.0 test
> restqa run . -x 'npm run start:express'

Server is running (command: npm run start:express)
🎯 The selected environment is: "local"
> 🏹 Target url: http://localhost:9999
.........

1 scenario (1 passed)
6 steps (6 passed)
0m00.048s (executing steps: 0m00.022s)

🎉 Test passed!

Migrate the app (revamp)

Your service did the job for several years. But one day, the architecture team decides to switch to another framework or another language. For this article, let's say they decide to use FastifyJs 🤩 now.

Let's first add a new deps: npm install fastify... and a new server:

// fastify-server.js
const fastify = require("fastify");

const app = fastify({ logger: true });

app.get("/hello", () => {
  return {value: "world"};
});

app.listen(9999);

Update test command:

{
  "scripts": {
    "start:express": "node express-server.js", // should be deleted ^^
    "start:fastify": "node fastify-server.js",
    "test": "restqa run . -x 'npm run start:fastify'" // update
  }
}

Run npm run test again:

➜  rqapi git:(fastify-version)npm run test 

> rqapi@1.0.0 test
> restqa run . -x 'npm run start:fastify'

Server is running (command: npm run start:fastify)
🎯 The selected environment is: "local"
> 🏹 Target url: http://localhost:9999
.........

1 scenario (1 passed)
6 steps (6 passed)
0m00.038s (executing steps: 0m00.016s)

🎉 Tada! Test passed again!

Sleep well!

Even if you decide to port the service on a different language (like Rust): it doesn't matter because you know that your tests will guarantee that the interface is not broken at all!

In opposition, if you only intend on unit testing, you could probably miss something.

RestQA will help the product team, QAs, developers to work on this source of truth!

As a final gift, add this command in your package.json:

{
  "scripts": {
      ...
      "test:dashboard": "restqa dashboard"
  }
}

Then run npm run test:dashboard, and npm run start:fastify in another terminal

Enjoy the view...

Feel free to visit the RestQA documentation if you want to learn more.

Photo credit by Fernando @cferdo