How To Test Web APIs with Perl and Cucumber

A tutorial on the world’s leading tool for BDD

Behavior-Driven Development is an agile software development process that encourages teams to use conversation and concrete examples to formalize a shared understanding of how the application should behave.

Cucumber Open is the world’s leading tool for BDD. The following tutorial demonstrates how to test web API using Test::BDD::Cucumber, Mojo::UserAgent and Test2::V0 modules.

Let’s get started 

First, we need to install Carton the Perl module dependency manager.

Now we can mock an API using Mojolicious lite web-framework.

  • Create a cpanfile:

  • Add Mojolicious web-framework to the cpanfile:

  • Install Mojolicious using carton:

  • Generate a Mojolicious lite app:

  • Edit the to return a json:

  • Start the server:

After the server is up and running we can move to the Cucumber part.

  • Create the Test::BDD::Cucumber file structure:

  • Add Cucumber and test modules to cpanfile file:

  • Install Cucumber and test modules:

Edit the feature file

Open the test_api/features/test_api.feature and add in this snippet:

To understand the above example you need some basic knowledge about the Gherkin verbs:

  • Given is used for a predefined action.
  • When is used for an action done by the user (making a request).
  • Then is used for evaluating the user action.
  • And duplicates the verb above
  • But duplicates the verb above

Also, as you can see, variables can be added to the feature file using the Examples table.

Running carton exec -- local/bin/pherkin test_api/ will display all the scenario steps in orange. This means that code to implement them was not written yet.

Writing step definitions

Open the test_api/features/step_definitions/ and add in this code snippet:

Running carton exec -- local/bin/pherkin test_api/ will display all the scenario steps in green, showing you that everything is ok.

Understanding “S” and “C” objects

The “S” and “C” objects are automatically created. “S” stores data for the duration of a scenario while “C” stores data for the duration of a step. Calling C->matches() will get you an array with all the matches the regex did in the current step. Instead, you can use regex capture groups like $1, if you prefer.

How everything worked together:

  • The Background code in the feature file executed the Before block in the step file which adds Mojo::UserAgent object and stores it in S.
  • Given qr/I query for the (\w+) data center/ This step adds the {dc => 'ro' } hash on S. Later this is used to add this ?dc=ro part to the request query.
  • When I make a GET request to the url /json

    This step does several things:

    1. reads the http verb and the url passed to him from the feature file and uses them to create Mojo::URL->new();
    2. makes a request using the object Mojo::UserAgent object stored in S.
    3. uses the Test2::V0 note() subroutine to document what happened.
    4. stores the request response in S->{result};
  • Then http code is 200
    This step reads the expect status code sent in from the feature file using a regex and compares it with whatever value we have stored in the response object.

Creating a library for common steps

If your team manages more than one API, you don’t want to have to rewrite the same tests over and over again.

Cucumber allows you to have steps libraries:

Now move this step Then qr/http code is (\d+)/to a new lib.

Then make sure you load the lib in test_api/features/step_definitions/

Running the code again should result in a pass.

Using tags

If a feature has more than one scenario and you don’t want to run them all at once you can use tags:

  • Call the json scenario:

  • Run all scenarios, except the xml one:

Generating an html report

To generate an html report use this:

The result should look like this:

If you click open a step you will see the details reported by note()

Integration with prove