Join egghead, unlock knowledge.

Want more egghead?

This lesson is for members. Join us? Get access to all 3,000+ tutorials + a community with expert developers around the world.

Unlock This Lesson
1×
Become a member
to unlock all features

Level Up!

Access all courses & lessons on egghead today and lock-in your price for life.

Autoplay

    Full End-To-End Testing in Cypress

    Brett CassetteBrett Cassette
    cypressCypress
    ^3.1.5

    Let's put all the pieces of the puzzle together. We'll assert on our database, XHR requests, frontend store, and UI in order to pinpoint the locations where our contract breaks down, and highlight our data lifecycle for our teammates.

    Code

    Code

    Become a Member to view code

    You must be a Member to view code

    Access all courses and lessons, track your progress, gain confidence and expertise.

    Become a Member
    and unlock code for this lesson
    Discuss

    Discuss

    Transcript

    Transcript

    Instructor: In this lesson, we're finally going to bring all of the pieces of the puzzle together that we've been building in order to do a full end-to-end test.

    This type of test is guaranteed to work in production and will test all levels of the stack -- the database, the API responses, XHR requests, frontend stores, UI -- all of it.

    To get started, we'll add a CY task for our DB snapshot. We'll take a snapshot of our to-dos in the backend. Basically, we are just asserting that we have an empty database.

    This is a clean slate that we should be working from and this should be true for all of our tests. We'll quickly pop open Cypress and see that our snapshot is indeed empty, so we're ready to get working.

    We always start our test with cy.server that way we can spy on our XHR request. Then we'll run a cy.seed so that we can get our backend into a predictable opening state. We'll say we have our to-dos.

    The first to-do can have the text, Hello World, which is the default. The second one can have the text, Goodnight Moon. We can say that it is completed so we can override that default.

    Then we can take a DB snapshot and assert that we are in the working state that we would expect to be in at this point, so it should deep equal.

    We'll pass in an array. We've seen these to-dos before, so I'm just copying them in, although we have different capitalization here this time. Let's save that out, take a look at our test. Everything is working as expected.

    We're ready to start visiting our page and asserting on each layer of the stack in order. We'll add a cy.route for our main page. This will be our preload API to-dos as preload. We will cy.visit the page. Then we will run a wait down here for the preload.

    Before we get the preload, we want to assert that there is no data in the store because we haven't actually loaded the preload yet. We can do cy.store on our to-dos, and we can say that it should be empty.

    After we wait for the preload, we can say the store on the to-dos should deep equal. This is the same snapshot up here, so I'm actually just going to make a const with our to-dos. There we go.

    Let's say that this is our to-dos, this is our to-dos here. You can see that we're stepping through the stack layer by layer. The database we know has these to-dos in it, we go visit the page.

    Before we've gotten the data from the backend, our frontend store is empty. We populate the frontend store with the data from the backend, then we can start making assertions on the UI.

    We can say get data cy equals to-do list. Then we can say .children should have length of two. We can make sure that before we wait for the preload, we also have length zero. We're building this up bit by bit here.

    The next layer that I would test here would be RXHR request. We could say its response.body should deep equal. It's going to deep equal these to-dos because here's where we're passing the to-dos from our database through to our frontend.

    We see this all happening in order. If we looked at our test, and we saw any location where the data flow broke down, we would be able to pinpoint it.

    We wouldn't have to wonder at the end here why aren't the to-dos painted on the UI. We wouldn't have some head-scratching error code here that doesn't make any sense because we had some breakdown in the store.

    If we don't see the data in the store, we will say, hmm, I guess something broke down in my reducer. Let's go take a look at that. In fact, let's actually just look at an example of what that would look like so we can see how valuable this is.

    Let's go to our reducer. Here's our to-dos loaded method where we actually get the to-dos from the backend in the preload. Let's just say that we returned the previous state so we didn't actually add the to-dos now to our page.

    Let's go back to our Cypress test. We'll run it, and we'll see that it's actually the store assertion that breaks down.

    Because we do wait, we do see the right response from the API, but we don't see it load into the store. We don't even make it to the UI layer because we're not expecting something that hasn't made it into the store to end up on our UI.

    This kind of end-to-end testing that emphasizes the flow of data through the application also has the benefit of making it a lot easier to onboard new team members because there's no question of where the data is going in the application or which parts of the application are touched by each individual feature.