DEV Community

Cover image for cy.request vs. cy.intercept
Walmyr
Walmyr

Posted on • Updated on

cy.request vs. cy.intercept

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.

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...
  })
})
Enter fullscreen mode Exit fullscreen mode

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:

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')
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

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.

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.

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')
})
Enter fullscreen mode Exit fullscreen mode

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.


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.

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')
  })
})
Enter fullscreen mode Exit fullscreen mode

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

Happy testing! 🕵️

Top comments (2)

Collapse
 
codingumr profile image
coding-umr

We can wait for element to appear on UI right. Then why to user intercept?

Collapse
 
walmyrlimaesilv profile image
Walmyr

You can use it as an extra layer to ensure the app is at the expected state before moving on to the next steps or assertions.