Mock Network Retries with Cypress

Brett Cassette
InstructorBrett Cassette
Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 4 years ago

How do you test complex interactions like network timeouts and retries? With Cypress, you can cover your bases, and assert that your application behaves reliably under unreliable conditions.

Recommended Homework:

  • Are multiple cy.route calls the only way to mock multiple requests in Cypress? The response key of cy.route can accept a function -- will that let us change the response based on request attempt?

Instructor: [00:00] Networks fail for all kinds of reasons, and it's common for our frontend code to retry in the face of network errors. Thanks to Cypress, we can test this behavior easily.

[00:10] Let's start by creating a new context for todo-creation retries. In here, we'll make two tests. The first we'll say is it retries three times. This will be the success case, where on the third time the retry is successful.

[00:29] Then, we'll say it fails after three unsuccessful attempts, and we'll go ahead and attach .only to our context, so this will be the only series of tests that we'll run.

[00:46] Let's go ahead and start off each test with a beforeEach context, which loads up the page in a known state. In this case we have the two to-dos that we've typically been using. Now we're ready to get started writing our test.

[01:00] Let's head into Cypress and see what the interaction looks like to create a new to-do. Typically, you're going to come here. We'll type "third to-do," and press enter, and we see that shows up on the page, so let's replicate that.

[01:15] We'll click on our Selector Playground, and we'll see that this is called "data-cy new to-do," so we'll go ahead and copy that. We'll paste it in and say that we type "third to-do," and then we'll use the special key Enter, and that will interact with the page for us.

[01:34] If we head back in the Cypress, we can see what the POST request is that's created here. We post API/todo, so let's go ahead and mock that out. That's going to be cy.route. The method is going to be POST. The URL is going to be API/to-dos.

[01:52] The first response is going to be a 500 status code, and we'll just reply with an empty response. Let's say this is createTodo, so we give it an alias. Then, we'll scroll down here and say cy.wait for addCreateTodo.

[02:11] Let's head back to Cypress. We'll see that we successfully waited on createTodo, and instead of hitting our real backend, which sent us a 201, now we're sending back a 500, which means that our frontend should start retrying.

[02:26] Let's go ahead and say that we're going to wait for this again, so we'll call this createTodo two, this will be the second version of the request. We can alternatively just wait on createTodo a second time, but this makes it a little more explicit what we intend.

[02:42] We'll come back to our test and see that createTodo two hangs indefinitely, and that's because our frontend doesn't yet do any retrying. Let's head back to our code and make sure we do some retries.

[02:54] This will be in todo-sagas. We'll import the retry-saga. The createTodo function is what actually creates the todo-now, so let's call this createTodo attempt, and we'll make sure this retries three times.

[03:11] Let's go ahead and use a try, because after three unsuccessful attempts, this will raise an error, and we'll yield using the retry-saga. We'll retry three times for one second each, and we'll retry using the createTodo attempt method, and then we'll pass it our action.

[03:31] If we're successful, we will yield the action AddToDoSuccess, and after three unsuccessful retries, we'll catch that error, and instead we'll yield a new action called AddToDoFail. If we return to Cypress, we'll see that we've now waited for both our first and second createTodo attempts and replied in both cases with a 500.

[04:00] Let's go ahead and finish our test. We'll move out the first two createTodo attempts to the beforeEach hook, because in both cases we're going to be using that, and then in the third createTodo attempt, in one case it'll be successful, and in the second case it will be unsuccessful.

[04:19] We can leave this 500 for the third createTodo for the unsuccessful attempt, and in the successful attempt, we'll just go ahead and change our status code to 201. We'll see that Cypress does in fact wait for each subsequent createTodo attempt, but we still haven't made any assertions.

[04:38] Let's go ahead and say that in the successful case, we will get the todo-list, and the todo-list.children.should have length three. In the unsuccessful case, let's just assume that it gets removed from the dump, so it'll have length two.

[05:03] Of course, this fails in our Cypress test, because we still have all three children on the dump, and the reason for that is because our reducer doesn't yet respond to the AddToDoFail method, which we added in our saga after three unsuccessful attempts.

[05:19] Let's go ahead and just finish this interaction. We'll uncomment this so it filters out the Todo, and we will go back to our test and see that it passed. With Cypress, network retries are easy to test, because for each XHR request, we can stub out a different response and then wait for them in order.