Is it possible to use just one tool to test everything? Although it may sound like a developer’s daydream, it is almost possible with Cypress, a JavaScript frontend testing framework. Cypress is built specifically for JavaScript frontend developers, and they can use it to start writing tests quickly without needing to add third-party dependencies or packages. This is a benefit missing from other tools like Selenium. In this tutorial, I will guide you through using Cypress to test an API.

This tutorial covers the following:

  1. Setting up Cypress framework
  2. Understanding how Cypress makes HTTP calls to APIs
  3. Understanding how to use Cypress cy.request() method

Prerequisites

To follow this tutorial, you will need to have a few things in place:

  • Basic knowledge of JavaScript and Git/GitHub
  • Node.js installed on your system (version >= 10.3)
  • A CircleCI account
  • A GitHub account

Using Cypress for API testing

Because Cypress is a standalone frontend testing tool, it makes HTTP requests on behalf of the web applications as it is executing. While it might appear that Cypress is making these requests from the browser, it actually uses Node.js as an engine to make the HTTP requests to the API server. It uses Node.js to return the responses received from the server.

Cypress Http Calls architecture diagram

Note: Cypress exposes the cy.request() method to make HTTP requests from within the framework. That is the method we will use in this tutorial to test our endpoints using Cypress.

Initializing the directory and setting up Cypress

First, create an empty directory and initialize an empty Node project by running these commands on the terminal:

mkdir testing-apis-with-cypress && cd testing-apis-with-cypress

The first command creates the directory while the second creates a Node.js project with default configurations. We will update the default config later in the tutorial. We will write our tests in the initialized Node project.

npm init -y 

You need to know that npm init -y creates a Node project with the default configuration and will not prompt you to customize the contents of the project’s package.json file. The package.json file will yield a different result if only npm init is run, because it will prompt you for the configuration of the Node project properties.

Now that you have an initialized directory, you can install the Cypress framework. Installation of Cypress happens just like installation of any other npm package, so you can install it with either:

npm install cypress

Or:

yarn add cypress

After Cypress has been installed, the next step is initializing the Cypress framework. This process will create the default Cypress directories that you need to write your tests and include packages, if that is required.

In the terminal, run the command cypress open. This command will initialize Cypress for the first time and create default Cypress directories and files.

Note: If you run into issues you may need to install Cypress globally.

Initializing Cypress

Setting up Git and pushing to CircleCI

Before you can start writing tests, you need to set up CircleCI. To begin, initialize a Git repository in the project by running the command:

git init

Next, create a .gitignore file in the root directory. Inside the file, add node_modules to keep npm generated modules from being added to your remote repository. The next step will be to add a commit and then push your project to GitHub.

Now log into the CircleCI and navigate to Projects. There will be a list of all the GitHub repositories associated with your GitHub username or your organization. Find the repository that you want to set up in CircleCI. In our case it is the api-testing-with-cypress project. On the Projects dashboard, select the option to set up the selected project. Select the option for using an existing configuration.

start-building-page

Now, on the prompt, select the option to start the building process. You can expect your pipeline to fail, because you still need to add the customized .circleci/config.yml file to GitHub.

start-building-prompt

Writing the CI pipeline

Once you have set up the CircleCI pipeline, it is time to add CircleCI to the local project. Start by creating a folder named .circleci in the root directory. Inside this folder, create a config.yml file. Add these configuration details to config.yml:

version: 2
jobs:
  build:
    docker:
      - image: cypress/base:14.16.0
        environment:
          ## this enables colors in the output
          TERM: xterm
    working_directory: ~/repo
    steps:
      - checkout
      - restore_cache:
          keys:
            - v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
            - v1-deps-{{ .Branch }}
            - v1-deps
      - run:
          name: Install Dependencies
          command: npm ci
      - save_cache:
          key: v1-deps-{{ .Branch }}-{{ checksum "package.json" }}
          # cache NPM modules and the folder with the Cypress binary
          paths:
            - ~/.npm
            - ~/.cache
      - run: $(npm bin)/cypress run
      - store_artifacts:
          path: ~/repo/api-testing-with-cypress

In this configuration, CircleCI pulls a Cypress Docker image from the environment and checks for any stored cache of our dependencies.

The next stage is to restore the cache if it exists, and update the application dependencies only when a change has been detected with save-cache. It runs the api-testing-with-cypress Cypress tests and stores the cached items in the artifacts that are in the ~/repo/api-testing-with-cypress directory.

Push your changes to GitHub and CircleCI should automatically start the building process. Here, since we currently do not have any tests yet, our pipeline will fail again. We will re-run this later on after we have added our tests. On the CircleCI dashboard, you should be able to see the created pipelines and the execution even without running tests in our repository.

Getting started with Cypress

For this tutorial, we will use an already built API that we have hosted on heroku to test our application. To do this though, we first need Cypress to understand what is the API Url that we will be using in our tests (Cypress refers to this as the baseUrl).

Setting thebaseUrl

Cypress framework has a configuration file cypress.json that is generated by the Cypress initialization process. The cypress.json file stores all the configuration variables that we supply, and this is where we will set our baseUrl. This is to ensure that we do not need to repeat the entire URL when we are running our tests. The baseUrl will be set as a configuration variable to our Heroku API URL as shown by the code snippet below in the cypress.json file.

{
   "baseUrl": "http://todo-app-barkend.herokuapp.com/"
}

Setting the run command

Now that we have our test URL ready, we need to create an easy way for us to run our tests using the terminal. To do this, we will add a script in the package.json file in the scripts section. To achieve this, we will need to add the test command below to the package.json file scripts section.

"scripts": {
   "test": "cypress open"
 },

This command tells Cypress to run in non-headless mode and open a browser when executing our tests.

Scripts in the package.json file automate commands and scripts that you would have had to run on the terminal. Instead you use npm to execute them while packaging them in a way that is understandable to the program being run.

Writing tests in Cypress

Cypress is a frontend testing tool, and it generates a lot of files and directories that are examples of how to write Cypress tests. For now we can ignore them.

Now that Cypress has been initialized and your CircleCI pipeline has been configured, it is time to start writing tests.

Writing your first Cypress API test

Remember, Cypress works by using Node under the hood to trigger HTTP requests and route them back to Cypress to make assertions and to verify that the expected calls were made. In the first test, we will make a GET request to our todo application to make sure that it actually returns the todo items to the API. Before we do this, we need to delete everything in the directory cypress/integration/. This directory contains the dummy scaffolds generated by Cypress initialization. In cypress/integration/, create another directory named api-tests. To make sure Cypress understands the actions of the test folder, update cypress.json file with this configuration value.

  {
d   "integrationFolder": "cypress/integration/api-tests"
  }

In the api-tests folder, create the todo.spec.js file, which is where we will write our tests. Here is the awesome bit: Cypress is self-sustaining, so we do not need any other external dependency to test our APIs.

Open the todo.spec.js file to set up the test on the API GET request for the todos. Add this code:

describe('TODO api testing', () => {
   let todoItem;
   it('fetches Todo items - GET', () => {
       cy.request('/todos/').as('todoRequest');
       cy.get('@todoRequest').then(todos => {
           expect(todos.status).to.eq(200);
           assert.isArray(todos.body, 'Todos Response is an array')
       });
   });
});

In this example, Cypress attaches baseUrl to the cy.request() method. This method makes a GET request to the hosted API endpoint to get our todo items, and validates that the API is being called by the test.

Note: When cy.request() has not been passed any HTTP request operation, it defaults to a GET request. That is why you do not need to tell the test that you are making an API GET request.

Executing Cypress API tests

To execute the first test and check that everything is running properly, run the command npm test in the terminal. We defined the npm test command earlier in our package.json. The Cypress dashboard will open.

Cypress dashboard

Any added test specs (files) will show up on the Cypress dashboard when we run the command to execute tests. Select the actual test and watch Cypress execute it.

Cypress dashboard executing tests

The dashboard shows that test execution was successful and we were able to get all our todo items. Cypress runs in a browser by default, so we can further verify all the elements that were returned from the test.

From the left side of the screen, click the last assertion that shows the returned arrays. This prints the output to the console. You can then right-click on the inspect element on the same window to open the browser developer tools. Click the Console tab to display the array of items that we are asserting in our test. There is a reason to celebrate, because we can verify that our API and our tests are working correctly.

Writing and adding more tests

Now that you are familiar with adding Cypress, you can add more tests. For example, you can test adding a new todo item and deleting a todo item.

   it('deletes Todo items - DELETE', () => {
       cy.request('DELETE', `/todos/${todoItem}`).as('todoRequest');
       // deletes Todo item with id = 9
       cy.get('@todoRequest').then(todos => {
           expect(todos.status).to.eq(200);
           assert.isString(todos.body, 'todo deleted!')
       });
   });
 
   it('Adds Todo item - POST', () => {
       cy.request('POST', '/todos/', { task: "run tests" }).as('todoRequest');
       // adds new Todo item by defining Todo name
       cy.get('@todoRequest').then(todos => {
           expect(todos.status).to.eq(200);
           cy.wrap(todos.body).should('deep.include', {
               task: 'run tests',
               completed: false,
           });
       });
   });

In this code snippet, we declare the type of HTTP request in the cy.request(). That is also where to declare any other arguments like the body for the POST request that creates a new todo item. Using these tests, you can verify that you can create a todo item using our API, get all the todo items created, and delete a todo item. Success! Our API is not broken.

Now we need to verify that the tests are also passing on the already created CircleCI pipeline. You have already initialized CircleCI, so you just need to commit and push your changes to GitHub. CircleCI will automatically run our tests.

Successful CircleCI execution

Again, success! All your have tests passed.

Conclusion

In this tutorial, you have set up Cypress and configured it to run API tests. You learned how to set the baseUrl for testing and configured the directories in the testing framework. You learned to use cy.request() to make different HTTP operations and how to use the Cypress test runner. I hope you and your team can benefit from what you learned in this tutorial. Enjoy developing your applications!


Waweru Mwaura is a software engineer and a lifelong-learner who specializes in quality engineering. An author at Packt, Waweru enjoys reading about engineering, finance, and technology. Learn more about him at: https://waweruh.github.io/.