cy.request vs. cy.intercept
Tags: cyrequest, cyintercept, cypressio, testing
Published: 2021-09-10 10:47:00

## Understand the differences between `cy.request` and `cy.intercept` and use them to write robust automated Cypress tests.

Some people make confusions between the `cy.request` and `cy.intercept` commands. However, they're quite different.

In this content, I will explain both.

## `cy.request`

As its name suggests, `cy.request` is the command used to perform requests at the network level, such as a `GET` or `POST`.

Such a command is useful when the application under test has a public API (application programming interface), which allows, for example, the creation of resources in an optimized way (i.e., without the need to go through the graphical user interface or GUI).

Let's imagine the following test case.

We have a sample clone of the Trello application, which allows you to create boards, board columns, and cards.

We want to test the card creation.

However, it is a prerequisite for the test that there is a board with at least one column.

Creating the board and column via GUI would be a waste, as there are probably GUI tests focused on these features already. Also, the test would be slow (it would spend more time creating the pre-conditions than testing the functionality of creating a card) and GUI-laden, which is not a good practice if you are looking for automated tests that scale along with the application.

Let's see how we could optimize this process using `cy.request`.

```js
describe('Trello App', () => {
  beforeEach(() => {
    cy.request({
      method: 'POST',
      url: '/boards',
      body: { title: 'TAT' }
    })
    cy.request({
      method: 'POST',
      url: '/boards/tat',
      body: { column: 'TODO' }
    })
  })

  it("creates a card in a board's column", => {
    cy.visit('/boards/tat')

    // The logic of card creation via GUI...
  })
})
```

In the above test, only the card creation takes place via GUI. In contrast, its pre-conditions (board and column creation) occur via API calls using the `cy.request` command in the `beforeEach` hook, making the test faster and more robust.

Another example of using `cy.request` is when we want to test an API itself.

Let's look at an example:

```js
describe('Trello API', () => {
  it('POST /boards', => {
    cy.request({
      method: 'POST',
      url: '/boards',
      body: { title: 'TAT' }
    }).should(response => {
      expect(response.status).to.equal(201)
      expect(response.body.title).to.equal('TAT')
    })
  })
})
```

In the above test, the focus is only on the request, without any interaction via the GUI, where we only guarantee the expected `status` and content of the `body`.

For more details about the `cy.request` command, visit the [**Cypress.io official docs**](https://docs.cypress.io/api/commands/request).

## `cy.intercept`

`cy.intercept` does not make a request but rather "listens" to requests on the network layer. If we "ask" Cypress to name a particular request that we expect to happen after some action, we can also "ask" it to wait for it before moving on when it notices that such a request occurred.

That is, `cy.intercept` is usually used in conjunction with two other commands, `.as` and `cy.wait`.

Here's an example of a GUI test that creates a card for a board (in the same Trello application), and ensures that, after creating the card (via GUI) a request to fetch the cards is triggered, which is in charge of loading the new card on the board into the frontend. That way, we can proceed with the verification only after such a request is finished.

```js
it("creates a card in a board's column", => {
  cy.intercept('GET', '**/boards/tat/cards').as('getCards')

  cy.visit('/boards/tat')
  cy.get('[data-cy="add-card-btn"]').click()
  cy.get('[data-cy="card-title-input"]').type('Bug #420')
  cy.get('[data-cy="card-title-description"]').type('Test bug #420 in production')
  cy.get('[data-cy="create-card-btn"]').click()

  cy.wait('@getCards')

  cy.contains('[data-cy="card"]', 'Bug #420').should('be.visible')
})
```

In the above test, we first define `cy.intercept` and give it an alias with the command `.as('getCards')`

Then comes the logic that creates the card via GUI, where we visit the page of a previously created board, click the button to add a new card, type the card's title and description, and finally, click the button that creates the card itself.

At this point, the `getCards` request should be triggered, so we use `cy.wait('@getCards')`  to only proceed to the next step (which asserts that the created card is visible) after the request ends.

Other things we can do with `cy.intercept` are:

- Reply to a request with a static `JSON` file defined in the `fixtures/` folder (i.e., we can mock the response)
- Handle the request ourselves
- Change the request's `statusCode` to simulate a server failure
- Simulate a network failure
- Simulate a delay in the request

For more details about the `cy.intercept ` command, visit the [**Cypress.io official docs**](https://docs.cypress.io/api/commands/intercept).

___

If you had paid attention to all the code examples, you might have noticed that they form a final version of themselves if we had put the first and the last code examples together.

That is, the test's pre-conditions are created via API calls (using `cy.request`), and the test itself creates a Trello card via the GUI (waits for the proper request to happen using a combination of `cy.intercept(...).as(...)` and `cy.wait(...)`).

Let's see the final version.

```js
describe('Trello App', () => {
  beforeEach(() => {
    cy.request({
      method: 'POST',
      url: '/boards',
      body: { title: 'TAT' }
    })
    cy.request({
      method: 'POST',
      url: '/boards/tat',
      body: { column: 'TODO' }
    })
  })

  it("creates a card in a board's column", => {
    cy.intercept('GET', '**/boards/tat/cards').as('getCards')

    cy.visit('/boards/tat')
    cy.get('[data-cy="add-card-btn"]').click()
    cy.get('[data-cy="card-title-input"]').type('Bug #420')
    cy.get('[data-cy="card-title-description"]').type('Test bug #420 in production')
    cy.get('[data-cy="create-card-btn"]').click()

    cy.wait('@getCards')

    cy.contains('[data-cy="card"]', 'Bug #420').should('be.visible')
  })
})
```

___

I hope the difference is clear. If not, leave a comment, and let's talk.

Happy testing! 🕵️