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()
})
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
}
...
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)
})
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)
Very interesting!
Thanks Juan Xavier! 👾