API Testing with Cypress: Part 2 - Creating your tests
Tags: tutorial, testing, javascript, node
Published: 2021-05-23 18:13:00

# Hello again guys!!

Moving forward with the subject we discussed in the [previous post](https://dev.to/murillowelsi/api-testing-with-cypress-part-1-5coe), let's focus on to the part that is most important to us, the **tests**.

# HTTP Methods

The resources of a microservice based applications can be manipulated in several ways. It is possible to create, update, delete them, and do other operations as well.

When we send a request for a service, we need to inform the URI to identify which resources we intend to handle. We also need to inform the type of manipulation we want to do on the resource. For this, we will use the HTTP protocol methods.

The HTTP protocol has several methods, each one has a different function. The most used are:

* **GET**: retrieve data for a resource.
* **POST**: create a new resource.
* **PUT**: make a change to a specific resource that already exists.
* **PATCH**: partially update a given resource.
* **DELETE**: delete a particular resource.

# Users Endpoint

Back to our target API - [ServeRest] (https://serverest.dev/), let's start by testing the user's endpoint:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gwdhehzhkhm28gdcbc1b.png)

## Goals

1. Use the GET method in `/usuarios` to retrieve all users data.
2. Validate query params.
3. Validate status code.
4. Validate the content of the response body.

**So, move your asses. Let's do it!**

>We will remove all the contents of the previous post from the Integration folder. That’s where our tests should be, so we’ll just leave what’s needed.

You can remove it manually or use the command below if you are lazy like me:

```
rm -rf cypress/integration/*
```

Now we are going to create a folder called `Usuarios`, which we will use to store all requests related to the User's endpoint. This will keep our code organized.

Inside the `Usuarios` folder, we will create our first test spec, called `GETusuarios.spec.js`
 
Now the project structure should look like this:

```ruby
├── fixtures
├── integration
│   └── Usuarios
│       └── GETUsuarios.spec.js
├── plugins
│   └── index.js
├── support
│   ├── commands.js
│   └── index.js
└── videos
```

# GET

## GETUsuarios.spec.js

Let's add the basic Mocha organization structure. You can use BDD style if you want:

* **describe** - `Given` -> *Test Suite name*
* **context** - `When` -> *Test Inner Suite name*
* **it** - `Then` -> *You should place your tests here!*

```javascript
/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send GET /usuarios', () => {
    it('Then it should return a list with all registered users', () => {
      // place your tests here
    });
  });

  context('When I send GET /usuarios passing id query param', () => {
    it('Then it should return only the filtered user', () => {
      // place your tests here 
    });
  });
});
```

# First Test Case

Now let's use Cypress method `cy.request` to make the HTTP request:

* method - GET
* url - API address + endpoint
!
```javascript
cy.request({ 
   method: 'GET', 
   url: 'https://serverest.dev/usuarios'
})
```

After that we will call the `.should` function, which I mentioned in the previous post. It will enable us to make multiple assertions on the yielded subject - **response** in this case.

```javascript
.should((response) => {
  // all your assertions should be placed here!!
});
```

Let's add a log to see what the '**response**' is returning in the body:

```javascript
cy.log(JSON.stringify(response.body))
```

In short, `cy.log` will access the  property 'body' of the 'response'. The `JSON.stringify` function will be used to transform the response body into a string.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2vv09awxeqvz6l3cshlm.png)

Run the command `cypress:open` and check what the log returns to us.

```
npm run cypress:open
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ph4aqt52okgiyuyf7n4b.png)

Awesome! We can conclude that our call is working correctly, as we are receiving the response body correctly and 200 status code (success).

Let's remove `cy.log` (we don't want trash in our tests) and add some status code and response body assertions.

We can validate the easiest one first, the **status code**:

```javascript
expect(response.status).to.eq(200)
```

Cool! The above code means that we expect the status code of the response to be equal to 200.

We can assert that the **quantidade** (quantity) key will always have the same number of the **usuarios** (users) array. Lets add this validation:

```javascript
expect(response.body.quantidade).to.eq(response.body.usuarios.length)
```

Let's validate that the `email` from `usuarios` field should not be null... 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cr2rj9tu5wa5kofrw930.png)

> Hmm, but `usuarios` is a list with three elements. So how do we do that? 

We need to pass which index from `usuarios` list we want to access to do this. For now, let's access the first object in the list, passing 'usuarios[0]':

```javascript
expect(response.body.usuarios[0].email).to.not.be.null
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m8nk4gk9hdrdlsnyfpgm.png)

Great, it worked! But what can we ensure that the 'email' key of all objects within the `usuarios` array is not null?

> Maybe duplicate the previous row, just changing the index?

```javascript
expect(response.body.usuarios[0].email).to.not.be.null
expect(response.body.usuarios[1].email).to.not.be.null
```

Yeah, it might even work. BUT what if we had a thousand of users inside this array, would we add a thousand lines to our code? 

> I don't think so.

To do a smarter assertion, we can use `Cypress loadash`, which offers us the `.each()` function:

```javascript
Cypress._.each(response.body.usuarios, (usuario) => {
  expect(usuario.email).to.not.be.null
})
```

This function work almost like a forEach(), going through the array and making the assertions now in each 'usuario' object from 'usuarios' array.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zk3goli8pg42ombbojnz.png)

Let's take the opportunity to add one last validation to `usuarios`. We expect each object to have all the keys ('nome', 'email', 'password', 'administrador', '_id'):

```javascript
expect(usuario).to.have.all.keys('nome', 'email', 'password', 'administrador', '_id')
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k1nresaic6d9q44ja511.png)

# Second Test Case

Moving to the next test case, we will send the same request as before, but this time passing a `query string` to filter only one user by `_id`:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fwurqlrg82emt0lhovg1.png)

Adding a validation to ensure that the name is always correct:

```javascript
context('When I send GET /usuarios passing id query param', () => {
  it('Then it should return only the filtered user', () => {
    cy.request({
      method: 'GET',
      url: 'https://serverest.dev/usuarios',
      qs: {
        _id: '0uxuPY0cbmQhpEz1'
      }
    })
      .should((response) => {
        expect(response.status).to.eq(200)
        expect(response.body.usuarios[0].nome).to.eq("Fulano da Silva")
      });
  });
});
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gcqrwaktquw5xe7py8d2.png)

# cypress.json - baseUrl

We are repeating the `url` parameter in both `cy.request()`. Add the following lines to your `cypress.json` file, so that it is not necessary to repeat this information.

*Set `video` as `false` as well. We don't want to Cypress record it for us.*

```javascript
{
  "baseUrl": "https://serverest.dev",
  "video": false
}
```

Okay, that was a good start and now our code looks like this:

```javascript
/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send GET /usuarios', () => {
    it('Then it should return a list with all registered users', () => {
      cy.request({
        method: 'GET',
        url: '/usuarios'
      })
        .should((response) => {
          expect(response.status).to.eq(200)
          expect(response.body.quantidade).to.eq(response.body.usuarios.length)
          Cypress._.each(response.body.usuarios, (usuario) => {
            expect(usuario.email).to.not.be.null
            expect(usuario).to.have.all.keys('nome', 'email', 'password', 'administrador', '_id')
          })
        });
    });
  });

  context('When I send GET /usuarios passing id query param', () => {
    it('Then it should return only the filtered user', () => {
      cy.request({
        method: 'GET',
        url: '/usuarios',
        qs: {
          _id: '0uxuPY0cbmQhpEz1'
        }
      })
        .should((response) => {
          expect(response.status).to.eq(200)
          expect(response.body.usuarios[0].nome).to.eq("Fulano da Silva")
        });
    });
  });
});
```

> Keep in mind that we are sending two requests to the same endpoint. This will need to be refactored, but we will come back to this case later.

# POST

## POSTUsuarios.spec.js

Moving on to the next HTTP method, we will now create a new file called POSTUsuarios.spec.js. In this file we will put all tests related to the POST method.

Create the test structure using the Mocha functions, exactly as we did in the GET file. Obviously, make changes to the descriptions according to the scenario `describe`, `context` and `it`.

```javascript
/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
    });
  });
});
```

This time the `cy.request()` function will be slightly different.
* The method will be `POST`.
* The url will remain the same as `/users` (no need to add the complete url, just add the resource).
* In the `body` param we'll add the necessary information to create a new user. To find out what information is needed, always consult the API documentation at [ServeRest] (https://serverest.dev/).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jdfmix79envi0u4te9p8.png)

Our payload will look like this:

```java
body: {
  nome: "Dumb John",
  email: "dumb.john@qa.com.br",
  password: "test",
  administrador: "true"
}
```

Let's take the opportunity to validate the success message and status code.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vznlz97cpmsr8zs6m5d2.png)

```javascript
/// <reference types="cypress" />

describe('Given the Users api', () => {
  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
      cy.request({
        method: 'POST',
        url: '/usuarios',
        body: {
          nome: "Dumb Joe",
          email: "dumb.joe@qa.com.br",
          password: "test",
          administrador: "true"
        }
      })
        .should((response) => {
          expect(response.status).eq(201)
          expect(response.body.message).eq("Cadastro realizado com sucesso")
        });
    });
  });
});
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v30a5awtav45w2sdv7n7.png)

> Success! Are you sure? Try to run the test again.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rrrv6u7lrax21fszrbkt.png)

When we run the test again it fails, but why? This is because, according to the business rule, it is not allowed to register a user with an already used email. Let's use a strategy to solve this problem.

Let's edit the file located in `plugins > index.js`

We will use lib `Faker` to create a different user for each request. Install `faker` using the `npm` command:

```
npm i -D faker
```

I named this `task` as `freshUser()` and inside it we will pass key/value using `faker` lib:

```java
freshUser() {
  user = {
    nome: faker.name.firstName(),
    email: faker.internet.email(),
    password: faker.internet.password(),
    administrador: "true"
  };
  return user;
}
```

The complete file will look like this:

```javascript
/// <reference types="cypress" />

const faker = require("faker");

/**
 * @type {Cypress.PluginConfig}
 */

module.exports = (on, config) => {
  on("task", {
    freshUser() {
      user = {
        nome: faker.name.firstName(),
        email: faker.internet.email(),
        password: faker.internet.password(),
        administrador: "true"
      };
      return user;
    }
  })
  return config
}
```

Now refactoring `POSTUsuarios.spec.js` to use` faker`. Create a variable called `fakeUser`.

```javascript
let fakeUser;
```

In order to create a different payload before each execution, we will use a **hook** called `beforeEach()`. Within this hook, we will call the function `cy.task()` throwing `user` created by the task to the `fakeUser` variable.

```javascript
beforeEach(() => {
  cy.task('freshUser').then((user) => {
    fakeUser = user;
    cy.log(JSON.stringify(fakeUser))
  });
});
```

> I also added a `cy.log()` to see the user being created for each request. *REMOVE THIS FROM YOUR CODE*.

Change the body parameter to receive `fakeUser` variable as well.

```javascript
cy.request({
  method: 'POST',
  url: '/usuarios',
  body: fakeUser
})
```

The refactored file looks like:

```javascript
/// <reference types="cypress" />

let fakeUser;

describe('Given the Users api', () => {
  beforeEach(() => {
    cy.task('freshUser').then((user) => {
      fakeUser = user;
      cy.log(JSON.stringify(fakeUser))
    });
  });

  context('When I send POST /usuarios', () => {
    it('Then it should create a new user', () => {
      cy.request({
        method: 'POST',
        url: '/usuarios',
        body: fakeUser
      })
        .should((response) => {
          expect(response.status).eq(201)
          expect(response.body.message).eq("Cadastro realizado com sucesso")
        });
    });
  });
});
```

We can now run the tests as many times as we want.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e5s7m40uop7tpx2wesjh.png)

I will finish this post now, because we already have a lot of information here!

> *Please keep in mind that just reading this post will not make you learn. Make your own code, practice, fail and fail again until you succeed!*

## Check this project repository on Github

{% github murillowelsi/cypress-api-tutorial %}

Feel free to add me on [LinkendIn](https://www.linkedin.com/in/murillowelsi/). Leave your comments, questions and suggestions.

Thanks for your attention, see you on next post!!