DEV Community

Cover image for Know this thing in Cypress
Phan Công Thắng
Phan Công Thắng

Posted on • Originally published at thangphan.xyz

Know this thing in Cypress

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:

  1. Type some inputs.

  2. 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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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')
})
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

This is the way Cypress does:

  • Find the p element with class username.

  • 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')
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
})
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

How Cypress command is run, can you guess it?

This is the way Cypress does for us:

  1. Visit the link and wait, retry until the successful state is achieved.

  2. Get the input with class name, wait, retry until the element is figured out.

  3. Type the content.

  4. Get the button with id submit, wait, retry until the element is figured out.

  5. 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')
Enter fullscreen mode Exit fullscreen mode

Take a look at the code above, we have one chain with 2 steps:

  1. find the div element with the class container

  2. find the p element with the class name

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 of expect.

Discussion (2)

Collapse
gktim profile image
gkTim

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.