Why Cypress!
We are working on a project that uses the Next.js framework. And in order to do unit tests for the project. We use Jest.
Jest is so great!
But when we want to test the unit page of Next.js, Jest feels a little bit harder to implement. Instead of using Jest, Cypress is an amazing selection for our goal.
In this article, we're going to figure some core concepts of Cypress.
Let's grab a drink and go forward!
before-after
When we run our test in Cypress environment, we are able to run debug or do some stuff in the browser.
For example:
I want to check the input whether it's typed or not, I need to do this flow:
Type some inputs.
Confirm the inputed value.
With this flow, the great thing in Cypress is I am able to travel forward or backward the action.
I can go backward to the input when it isn't typed. Or just go forward to the input when it is typed.
In order to better of understanding, I'm going to demo the before-after
in Cypress.
This is the flow:
Having an input that isn't typed any words.
Type some words in the input.
Observe the
after
state in Cypress.
Step1: Create a page in Next.js
There are an input element and a label element in our page.
export default function BeforeAfter() {
return (
<div>
<form>
<label htmlFor="username" aria-describedby="userName">
Username:
</label>
<input id="username" aria-describedby="userName" />
</form>
</div>
)
}
Step2: Write Cypress Code
First of all, create a new file cypress.ts
under the cypress
folder in order to connect with Cypress
.
it('by default, the number of cars is shown on the screen', () => {
cy.visit('/before-after')
cy.findByLabelText(/username/i).type('Henry Ford')
})
Next, we go to the BeforeAfter
page, find the label username
(input element), and type Henry Ford
for it.
Now, on the left side, you are able to click the type
state and pin it. The before
and after
state immediately appears. Press before
and after
button on the screen, we can see the content of the input is changed before
typing and after typing.
Query DOM
Asynchronous
Did you be familiar with Jquery?
Did you do some query like $(.className)?
Using Jquery, we are able to query the element in a synchronous way, and if we don't find the element, Jquery will return null for us.
But in Cypress, we don't do it synchronously, we find the element in the asynchronous way.
If the element isn't found the first time, Cypress will retry to find the element in the fixed time(4000ms).
After this fixed time, if Cypress still don't figure out the element, we will receive an error.
For ease of understanding, let's describe through the example below!
cy.get('p.username')
This is the way Cypress
does:
Find the
p
element with classusername
.If the element is found just yield it.
If the element isn't found, find the element again.
In 4000ms if
Cypress
still doesn't see the element, throw error for us.
Content
We also can query the element from the content of the element using contains
function.
cy.contains('awesome')
We tell Cypress
to find the element that has the word awesome
on the screen.
Command Running
Command Asynchronous
All commands in Cypress are asynchronous. Let's discover it through the example below!
let userName = undefined
cy.get('p.name').then(($) => {
userName = ...
})
if(userName) {
// do some thing
} else {
// do some thing
}
Take a look at the code above, the userName
value always is undefined
because the command cy.get('p.name')
is asynchronous, it still doesn't finish!
How to resolve the problem above?
We just move the if else
condition to .then
that the element is found.
let userName = undefined
cy.get('p.name').then(($) => {
userName = ...
if(userName) {
// do some thing
} else {
// do some thing
}
})
Command Asynchronous Step By Step
Imagine that we have the code below:
cy.visit('/user-name') // 1
cy.get('input.name').type('Henry Ford') // 2,3
cy.get('button#submit').click() // 4,5
How Cypress
command is run, can you guess it?
This is the way Cypress
does for us:
Visit the link and wait, retry until the successful state is achieved.
Get the input with class
name
, wait, retry until the element is figured out.Type the content.
Get the button with id
submit
, wait, retry until the element is figured out.Trigger the
click
event.
In the flow above, if Cypress
doesn't find the element in the retry process, it throws the error for us.
In the code above, we don't stop visiting the about
route, we also wait for the load event finishes, we have DOM, and we can do some stuff after that.
Assertions
default assertions
The great thing in cypress is default assertions, what's default assertions?
cy.get('div.container').get('p.name').contains('your name')
Take a look at the code above, we have one chain with 2 steps:
find the
div
element with the classcontainer
find the
p
element with the classname
With this chain, by default Cypress auto add assertion, assert the class container
exists, and the class name
exists.
should or expect
In Cypress, we have two ways in order to assert the behavior that we expect.
should
expect
Using should
is preferred to use, because it's short, easy of watching the behavior happened before we have this goal.
There are a ton of property
that should
is supporting, take a look at the Cypress
doc to see in detail.
Recap
We just learned about the core concept in Cypress, Let's recap some key points!
- We can debug and travel all of the states in Cypress.
- Query the element in Cypress is asynchronous.
- Commands in Cypress is run asynchronously.
- By default, Cypress automatically has default assertions.
- Prefer to use
should
instead ofexpect
.
Top comments (1)
For E2E-Testing also have a look at playwright.
Pretty good E2E testing lib from Microsoft.
In my opinion the api is cleaner then cypress and you can debug tests in your ide.