DEV Community

Santiago Salazar Pavajeau
Santiago Salazar Pavajeau

Posted on

Easily test React-Redux with Cypress

Cypress is a very visual Javascript testing framework that can do end-to-end, integration and unit tests. One of its cool features, is the ease of querying elements with the tool on the test browser, especially when using frameworks like Semantic UI, Bootstrap, etc. because UI components are pre-defined and it's harder to know the underlying HTML elements making up the components.

Quickly testing from the user's perspective

    it('New project modal form is controlled', () => {

        cy.get('.eight > .button')
          .click()

        cy.get(':nth-child(2) > .ui > input')
          .type('Test title')
          .should('have.value', 'Test title')

        cy.get('textarea')
          .type('Test description')
          .should('have.value', 'Test description')

        cy.get('#new-project')
          .click()
   })
Enter fullscreen mode Exit fullscreen mode

Cypress will give you the selectors like: cy.get('.eight > .button') (from their browser testing select feature), which give access to the element from the DOM. In this case, it is an input element so we can .type something into the input, check the updated value it .should have, and finally .click() on the submit button.

Testing the store and the backend API

Cypress allows for end-to-end testing having access to asynchronous calls, and also the application environment. However, having access to our redux store can be a little tricky.

First, because this data is only meant to exist inside the React application, and making it available to an external environment like Cypress can be insecure.

...
export const store = createStore(reducer, 
  applyMiddleware(thunk)
  );

...

if (window.Cypress) {
  window.store = store
}
...
Enter fullscreen mode Exit fullscreen mode

Second, if the application state updates asynchronously, this requires the tests to only run after the state has updated. Cypress has several ways to deal with this like only testing the DOM elements on the surface instead of the Redux store underneath or testing the backend with asynchronous requests tests.

But if we want to build an application starting from Redux, and focus on developing tests for the store we can use the cypress-pipe package, which helps Cypress wait to test the store when it's actually updated.

    it('Adds a new user', () => {
      cy.visit('/projects')
      cy.get('[href="#/new-project"]').click()
      cy.get(':nth-child(2) > .ui > input').type('Test Title')
      cy.get('textarea').type('Test Description')
      cy.get('[type="submit"]').click()

      cy.request(`${URL}people`)
        .then((response) => {
          expect(response.status).to.eq(200)
        })

      const getProjects = (window) => window.store.getState().projects

      cy.window().pipe(getProjects).should('have.length', 5)
    })
Enter fullscreen mode Exit fullscreen mode

Here we make a test of a new project feature on the site. So the test types the title and description and then submits it. We can have access to the store through the window object on the Cypress browser environment and test our Redux state with the right timing.

Feel more than welcome to reach out with any ideas/comments at Linkedin or Twitter, or check out my portfolio.

Top comments (2)

Collapse
 
xendke profile image
Juan Xavier Gomez

Very interesting!

Collapse
 
santispavajeau profile image
Santiago Salazar Pavajeau

Thanks Juan Xavier! 👾