Test Driven-Development with E2E Tests
Don't write the entire flow, do tiny steps.

Just a guy who loves to write code and watch anime.
Search for a command to run...
Don't write the entire flow, do tiny steps.

Just a guy who loves to write code and watch anime.
Interesting read! 👌 Thanks for sharing!
Qualities that make outstanding builders.

Bipedal vs quadrupedal: how many legs This is about the number of legs the creature walks on. Bipedal. Two legs. Humans, ostriches, T-rex, kangaroos, most fantasy humanoids. Quadrupedal. Four legs. Do

Intro You hear "lerp" and "smoothstep" everywhere in game dev. They sound like math jargon. They're not. Both are small tools that do the same job: smoothly move from one value to another. The problem

What is Kinematics Kinematics is the math field. It's the study of how things move without worrying about forces (which would be dynamics). FK and IK are the two branches: forward kinematics and inver

Intro Textures are usually the biggest cost in a 3D scene. Memory, bandwidth, and load time all get eaten by them. Resizing them is the obvious lever. There's more. This post is about the less obvious

I write my tests using Cypress. If you know me, you know I love TDD. In this article, I will share my experience doing TDD with Cypress.
I love Cypress. It is the best E2E testing framework. I use it in combination with Testing Library in order to use accessible queries which don't just increase the confidence because you aren't testing implementation details, but the experience is a lot better.
I love how the watch mode is fast and responsive. The debugging experience is also fantastic, and the recording, being able to view the entire test and stop at points to see how things looked at a specific time during the test.
Cypress is phenomenal.
Doing TDD with Cypress can be hard. Back in the day, I used to write the entire test suite before writing the implementation of the feature, it was a pain. It was a pain for a few reasons:
This was a pain. I have been thinking of how to do it differently, more effectively, and I remembered, it is all about tiny steps.
I don't write the entire test now. I write a bit of it, make sure it fails, and then I go and write the code to make the test pass. Lastly of course, once the test has passed, I look to if I can refactor any code.
I love doing it this way, it has been such a joyful experience. I feel less nervous and anxious when writing the implementation of the feature.
An example always helps to clarify things.
Below we have an entire flow where a user signs up.
it('Should be able to sign up', () => {
cy.visit('/')
cy.findByRole('link', { name: 'Kamuy' }).should('be.visible')
cy.findByRole('heading', { name: 'Sign In' }).should('be.visible')
cy.findByText(
"If your account doesn’t exist, we'll create one, otherwise you just sign in."
).should('be.visible')
cy.login(newUser)
cy.findByRole('button', { name: 'Sign In' }).click()
cy.findByRole('status')
.findByText(SIGNED_UP_SUCCESS_MESSAGE)
.should('be.visible')
})
There is quite an amount of code we would have to write to get this to pass, let's break it down into chunks, and do tiny steps!
it('Should be able to sign up', () => {
cy.visit('/')
// Assert the UI
cy.findByRole('link', { name: 'Kamuy' }).should('be.visible')
cy.findByRole('heading', { name: 'Sign In' }).should('be.visible')
cy.findByText(
"If your account doesn’t exist, we'll create one, otherwise you just sign in."
).should('be.visible')
// Login
cy.findByLabelText('Email').type(user.email)
cy.findByLabelText('Username').type(user.username)
cy.findByLabelText('Password').type(user.password)
cy.findByRole('button', { name: 'Sign In' }).click()
// Toast message
cy.findByRole('status')
.findByText(SIGNED_UP_SUCCESS_MESSAGE)
.should('be.visible')
})
Instead of writing the entire flow, we can begin with the first part which asserts the UI on the home page.
it('Should be able to sign up', () => {
cy.visit('/')
// Assert the UI
cy.findByRole('link', { name: 'Kamuy' }).should('be.visible')
cy.findByRole('heading', { name: 'Sign In' }).should('be.visible')
cy.findByText(
"If your account doesn’t exist, we'll create one, otherwise you just sign in."
).should('be.visible')
})
We begin by writing the implementation for this part of the test, and getting it to work. It is much easier than having so much in your head and trying to figure things out, especially since we're dealing with authentication above.
Don't write the entire test, write a part of it followed by the implementation and a refactoring, then proceed to the next part of the test.
Doing TDD when writing E2E Tests can be hard if you do it the wrong way. By breaking it down into multiple steps, everything just becomes much better.