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 you 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.

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

Our tutorials are platform-agnostic, but use CircleCI as an example. If you don’t have a CircleCI account, sign up for a free one here.

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 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. You will update the default config later in the tutorial, and you 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. Cypress is installed just like any other npm package. You can install it with:

npm install cypress --save-dev

Or with:

yarn add cypress

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

In the terminal, run the command npx cypress open. This command initializes Cypress for the first time and creates default Cypress directories and files.

You will be prompted to choose between E2E or component tests. For this tutorial, you are writing E2E tests, so click the E2E Testing button to get started.

Initializing Cypress-Choose-test-type

Cypress will notify you about the different files it will generate to ensure proper configuration for end-to-end testing. Click Continue to proceed.

Initializing Cypress-config

Next, you will need to select a browser. The available options may vary depending on which browsers are installed on your computer. For this tutorial, use the “Chrome Canary” browser.

Initializing Cypress-choose-browser

Cypress will start and prompt you to generate your initial specification. You haven’t created any test files yet. Before you do that, set up git and CircleCI configurations to automate the testing process.

#Initializing Cypress-final-setup

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. Then add a commit and push your project to GitHub.

Note: GitLab users can also follow this tutorial by pushing the project to GitLab and setting up a CI/CD pipeline for their GitLab repo.

Writing the CI pipeline

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.1'
orbs:
  cypress: cypress-io/cypress@3
workflows:
  use-my-orb:
    jobs:
      - cypress/run:
          cypress-cache-key: custom-cypress-cache-v1-{{ arch }}-{{ checksum "package.json" }}
          cypress-cache-path: ~/.cache/custom-dir/Cypress
          cypress-command: npx cypress run
          node-cache-version: v2

In this configuration, CircleCI pulls a Cypress orbs and uses the configuration to run Cypress tests.

Commit these new changes, push again to GitHub. Log into CircleCI and navigate to projects. There will be a list of all the GitHub repositories associated with your GitHub username or organization. Find the repository that you want. In this case use the api-testing-with-cypress project. On the Projects dashboard, select the option to set up that project. Click Use the .circleci/config.yml in my repo. Then click Set Up Project to start the first build process. You can expect your pipeline to fail, because you have not yet added any Cypress test specs.

start-building-page

Getting started with Cypress

This tutorial uses an pre-built API that hosted on Heroku to test the application. To get started, Cypress requires the API URL that you will be using in your tests. Cypress refers to this as the baseUrl.

Setting the baseUrl

The Cypress framework configuration file is generated by the Cypress initialization process. This file, cypress.config.js, stores all the configuration variables, and this is where to set your baseUrl. The baseUrl will be set as a configuration variable to your Heroku API URL as shown by this code snippet from the cypress.json file:

{
   "baseUrl": "https://aqueous-brook-60480.herokuapp.com/"
}

Setting the run command

Now that your test URL is ready, you can create an easy way to run tests using the terminal. Add the test command 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 the 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 you can ignore these.

Now that Cypress has been initialized and your CircleCI pipeline has been configured, you can start writing tests.

Writing your first Cypress API test

Remember, Cypress works by using Node under the hood to trigger HTTP requests. Node routes them back to Cypress to make assertions and to verify that the expected calls were made. In the first test, you will make a GET request to the todo application to check that it returns the todo items to the API.

Create a new directory named integration within the Cypress folder (cypress/integration/). In cypress/integration/, create another directory named api-tests. To make sure Cypress understands the actions of the test folder, update the cypress.config.js file with this configuration value:

  {
    "specPattern": "cypress/integration/api-tests/*.spec.{js,jsx,ts,tsx}"
  }

In the api-tests folder, create the todo.spec.js file, which is where you will write your tests. Here is the awesome bit: Cypress is self-sustaining, so you do not need any other external dependency to test your 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. You defined the npm test command earlier in package.json. The Cypress dashboard will open.

Cypress dashboard

Any added test specs (files) will show up on the Cypress dashboard when you 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 you were able to get all the todo items. Cypress runs in a browser by default, so you 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 are asserted in the test. There is a reason to celebrate, because you can verify that both the API and your 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.

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

   it('deletes Todo items - DELETE', () => {
       cy.request('DELETE', `/todos/${todoItem}`).as('todoRequest');
       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,
           });
       });
   });
});

This code snippet declares the type of HTTP request in the cy.request(). That is also where to declare any other arguments, including 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 your API, get all the todo items created, and delete a todo item. Success! Your API is not broken.

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

Successful CircleCI execution

Again, success! All your tests have 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 got some experience using 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 life-long learner who specializes in quality engineering. He is an author at Packt and enjoys reading about engineering, finance, and technology. You can read more about him on his web profile.

Read more posts by Waweru Mwaura