DEV Community

Gianluca La Manna
Gianluca La Manna

Posted on

Cypress - Automation test with React

Lately at work I was tired of writing code that would end up on a spaghetti code style code base. My code probably would have made a negative contribution to that code base as well.
The basic problem was the company culture. So in the last period I decided to change jobs. Being a frontend developer for a company where 'value' is at the center of everything.

And so I wrote one of my first tests in Cypress.
Tests give us reliability and safety, bringing a lot of value. Unfortunately, this is not always immediately perceived by customers or by the companies where one works. They are often seen as a waste of time. But they are a long-term investment.

With Cypress we can write e2e tests that allow us to verify functional properties and non-functional properties, such as performance or reliability. We can study the flows of our application and verify that everything works as we intended.

Below I will show an example of a test made with Cypress on a React project with NextJS.
We install Cypress on our code base. I will be using yarn.

yarn add cypress -D

Add Cypress to the package.json scripts field:

"cypress": "cypress open"

So we can run in our CLI

yarn cypress

Screen Cypress testing tool

We choose E2E testing
You can look through the generated examples and the writing your first test.

If we use typescript, under the cypress folder of our project we add a tsconfig.json file:

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["es5", "dom"],
    "types": ["cypress", "node"]
  },
  "include": ["**/*.ts"]
}

Enter fullscreen mode Exit fullscreen mode

Cypress folder structure

Now let's write a test that simulates adding an item to the cart and checking out the product.

/// <reference types="Cypress" />
export {};

context('Step checkout', () => {
  const mockObjectForm = {
    email: 'test@test.com',
    name: 'Pippo',
    surname: 'Pluto',
    address: 'Via Test',
    city: 'Palermo',
    province: 'PA',
    zip: '90100',
    phone: '123456789',
  };

  it('should fill the checkout form and complete the order', () => {
    cy.stepSelectProduct();
    cy.fillFormCheckout(mockObjectForm);

    cy.get('[type="radio"]').check('wire_transfers');
    cy.contains("Conferma l'ordine").click();

    cy.get('.checkout__success__title')
      .should('be.visible')
      .scrollIntoView({ duration: 500 })
      .contains('Il tuo ordine è stato ricevuto');
  });
});

Enter fullscreen mode Exit fullscreen mode

cy.stepSelectProduct() and cy.fillFormCheckout(mockObjectForm) are commands defined on the file cypress/support/commands.ts
We can see them as separate functions to be called as needed.

The first is to simulate product selection and addition to the cart. The second is to fill in the shipping address form.

/// <reference types="cypress" />

declare global {
  namespace Cypress {
    interface Chainable {
      fillFormCheckout(value: PersonalInfo): Chainable<Element>;
      stepSelectProduct(): Chainable<Element>;
    }
  }
}

export type PersonalInfo = {
  email: string;
  name: string;
  surname: string;
  address: string;
  city: string;
  province: string;
  zip: string;
  phone: string;
};

Cypress.Commands.add('fillFormCheckout', (personal: PersonalInfo) => {
  cy.get('input[name="customerEmail"]').should('be.visible').type(personal.email);

  cy.get('input[name="policyConsent"]').should('be.visible').check();

  cy.get('input[name="shippingAddress.firstName"]').should('be.visible').type(personal.name);
  cy.get('input[name="shippingAddress.lastName"]').should('be.visible').type(personal.surname);
  cy.get('input[name="shippingAddress.line1"]').should('be.visible').type(personal.address);
  cy.get('input[name="shippingAddress.city"]').should('be.visible').type(personal.city);
  cy.get('input[name="shippingAddress.stateCode"]').should('be.visible').type(personal.province);
  cy.get('input[name="shippingAddress.zipCode"]').should('be.visible').type(personal.zip);
  cy.get('input[name="shippingAddress.phone"]').should('be.visible').type(personal.phone);
  cy.contains('Invia').click();
});

Cypress.Commands.add('stepSelectProduct', () => {
  cy.visit('http://localhost:3000/');

  cy.contains('Aggiungi al carrello').scrollIntoView({ duration: 500 }).click();

  cy.contains('Carrello').click();

  cy.get('a[href*="checkout.html"]').scrollIntoView({ duration: 500 }).click();
});

Enter fullscreen mode Exit fullscreen mode

cy.visit places us on the starting page.
The relative get and contains are used to refer to the elements of the DOM through classes and placeholders and simulate the various events on the page through click() and check().
To refer to elements it is recommended to use placeholders because they fully reflect the flow that the user will perform when filling out a form or clicking on a button.
Using classes makes our tests more fragile. We should write tests that are closer to the user than the developer.

As an alternative to the 'get' it is possible to use the testing-library methods by installing the appropriate library:

testing-library/cypress

In this way we can use for example the cy.findByPlaceholderText('value') rather than cy.get('input [name = "value"]')

The test will start automatically when the file is saved. If our tests are not long, we will use them more often to test the new features introduced with each new feature or refactoring activity. It is correct to create tests that are not too long and we do not necessarily have to reach 100% coverage.

See ya 👋🏻
Thank you for reading ✨

Top comments (1)

Collapse
 
buddhadebchhetri profile image
Buddhadeb Chhetri

WOW this is really amazing get to know something new !!