Transcripted Summary

You might notice that there's something strange happening in my test here.


it('Intercept requests', () => {

  cy
    .visit('/')

  cy
    .get('[data-cy=board-item]')
    .should('have.length', 0)

});

I'm making an assertion that there should be 0 board items in my board list.

I can even run these a couple of times, and it's still passing. So why is that happening?

We can find the answer if we take a closer look into our timeline. When I hover over my get element, you can see that it tries to search for a board item before the data is fully loaded.



And that is a problem, because we are getting a false positive. This test should definitely fail.

What we need to do is to make our get command wait until the list is fully loaded.

While Cypress does have a wait command which we can use to wait for a couple of seconds, this is not really the best approach.



We can get to a situation where 2 seconds may not be enough.

But also, if we have good network conditions, we are preventing our test from finishing sooner. These 2 seconds don't feel like much but imagine having thousands of end to end tests. That adds to quite a lot of idle time.

Ideally, what we want to do is to wait for the server to give us a response, and we can do exactly that.

I'll delete this wait command, and add a new command called intercept.

The reason I'm adding it before we make a visit is that I want to wait for this GET /api/boards command that will happen immediately when we visit our app, so I want to make sure that it is properly matched before that happens.

If I added my intercept command after the visit, it might be too late.

In some ways, the intercept command resembles our request command. We can add a method that will be GET, and we can match a URL that will be our /api/boards.


  cy
    .intercept({
      method: 'GET',
      url: '/api/boards'
    })

I saved my tests now to see what's different.


In the test runner, I can see I have a new item, “ROUTES”.



This contains all the URLs that I have matched with my intercept command.

We can see the details such as, method, URL, whether it is stubbed or not. And we can see the alias and number of goals. We can see that our “GET” request was called exactly once.


Let's now fill this empty space and add an alias.

I'm going to add an alias (.as) with the name boardList.


  cy
    .intercept({
      method: 'GET',
      url: '/api/boards'
    }).as('boardList')

And when I save my test now, you can see my request highlighted in the timeline.

As you can see, our test is still falsely passing.



What we need to do is not only intercept our command, but actually wait for that command to happen.

So, I will add a wait command, but instead of passing a number, I'm going to reference the alias of our intercepted request. And whenever I write an alias, I prefix it with the @ symbol, so that would be @boardList.


  cy
    .wait('@boardList')

When I now save my test, you can see that Cypress has first executed our wait command, and only when the response from our server came back, it proceeded to the get command, and now it is properly failing.



Let's now change our test, so it will be passing (by changing the length to 1 instead of 0).

Let's open the console and take a look into the details of our request.

When I click on my wait command, you can see that it has yielded some information.

Inside it, we can see that we have our request info available. We can also see the response body, status code, and so on.



Since we have this information available, we can do a little API testing.

Let's see for example, if we can check the status code. So, I'm going to type its, which is going to yield the information from my wait command.

And I will look into the response.statusCode. And for that, I want to assure that it is equal to “200”.


  cy
    .wait('@boardList')
    .its('response.statusCode')
    .should('eq', 200)
    })

I'm going to close the console.

When I save my test now, you can see that in my test it's passing, and I'm checking the response status code.



Let's play with this for a while and try to do something else.


Let's try to match the request that happens when we create the board.

When we do that, I can see that there's a “POST” request to the same URL. So, let's just change our intercept command — that's going to be “POST”.

The URL stays the same, but I'm going to give it another alias, and that would be “createBoard”, as that is more informative.

Now, after I visit my application, I want to get my “create-board” item. I will click on it.

Then I'll get my “new-board-input” element. And I will type the name of the board, which will be “launching a rocket”. Then I'll hit “enter”.

So, I want to wait for the “@createBoard”.


it('Intercept requests', () => {

  cy
    .intercept({
      method: 'POST',
      url: '/api/boards'
    }).as('createBoard')

  cy
    .visit('/')

  cy
    .get('[data-cy=create-board]')
    .click()

  cy
    .get('[data-cy=new-board-input]')
    .type('launching a rocket{enter}')

  cy
    .wait('@createBoard')

});

We’ll now delete the rest, and see if everything is okay.

When I save that, I see that my test is working fine.



Let's write a couple of tests. And right, now I'm going to choose a different approach.

I will use a then command, and then use a function, and inside the function I'm going to write a couple of expect assertions.

So, expect, for example, board.response.statusCode, to equal “201”.

And let's also say that I want to expect that the board.request.body.name is equal to our text, which is “launching a rocket”. I want to make sure that my app is actually sending the proper data to the server.


  cy
    .wait('@createBoard')
    .then( (board) => {
      expect(board.response.statusCode).to.eq(201)
      expect(board.request.body.name).to.eq('launching a rocket')
    })

When I now save my test, I'm getting a good response.

I have my 201 response, and it seems that I'm also sending the right request.



This way we can combine UI and API tests into one single end to end test.

Let's recap what we have done here.

First, we used intercept to match an HTTP request and gave it an alias.

Then we made sure that Cypress is executing commands in proper order. So, we first waited for an HTTP request to give us a response, and then we proceeded with other action. That way we reduce the chance of flakiness and false positives of our tests.

Also, when we have a request intercepted, we can write tests for it. We can either test the status code, request or response body, and also, we can use then to make multiple assertions at once.



Resources



© 2024 Applitools. All rights reserved. Terms and Conditions Privacy Policy GDPR