A quick look at Cypress component testing

Posted on Wed 29 June 2022 in Coding

Co-authors: Andreas Cederström and Sebastian Porling.

Cypress has recently released support for component testing in their 10.0 release. You read the announcement here. We decided to take a quick look and try it out, to gather some first impressions and see how it can fit into our development workflow.

The new Cypress app helped us get up and running in a very elegant way. Cypress made it easy for us to write the first test, by providing commands for cloning a demo repository with sample code. Once that was in place, we opened Cypress, selected the web browser to use and were met by a guide for creating the component test configuration based on frontend framework (React or Vue) and bundler (Webpack or Vite). We then proceeded to create the first component test case.

Writing a component test was just as easy as writing regular Cypress tests. There’s no special magic needed apart from importing the appropriate mount function for the selected frontend framework, in our case React. Unlike regular Cypress tests, there is no need for starting your application. It’s sufficient to import and mount you component under test. This is the first test we wrote for the sample component (the base cases were already covered by sample tests):

import { mount } from 'cypress/react'
import Stepper from '../../src/Stepper'

const stepperSelector = '[data-testid=stepper]'
const incrementSelector = '[aria-label=increment]'

it('should be able to step up by one', () => {
  mount(<Stepper />)
  cy.get(incrementSelector).click()
  cy.get(stepperSelector).should('contain.text', 1)
})

This looks almost identical to a “regular” unit test with Jest or Karma. The main upside as we see it is the instant visual feedback since the test runs in a regular browser. This is what a test run might look like:

Another benefit is that upon test failure you get a screenshot of the problem. This is not a specific feature of component testing, of course, but it’s one more advantage compared to a regular unit test.

We noticed that running the component tests was fairly quick, but it does take longer time than running typical component unit tests. For example, running 120 tests took 7 seconds with Cypress open and 11 seconds with Cypress run.

As always, using demo code is simple since everything is laid out for you. When we tried to write a component test in one of our existing projects, it became obvious what we already knew:

The first test is always the hardest!

In the project we use Parcel, and since Cypress only supports Webpack and Vite, we had to migrate the bundler configuration first. Furthermore (and this is not a Cypress problem of course), we were faced with a number of errors that turned out to be caused by the lack of a router context. Once we wrapped the component in a router context, we could proceed:

import { PageHeader } from "../../src/Components/PageHeader"
import { mount } from "cypress/react"
import React from "react"
import { MemoryRouter, Routes, Route } from "react-router-dom"

describe("PageHeader", () => {
    it("Can be mounted", () => {
        mount(
            <MemoryRouter initialEntries={["/"]}>
                <Routes>
                    <Route
                        path={"/"}
                        element={<PageHeader />}
                    />
                </Routes>
            </MemoryRouter>
        )
    })
})

Cypress presented the errors clearly, so it was quite easy to understand what was going on. Had we used Cypress component testing from the start in a TDD fashion, we would have been faced with the same problem, but since TDD encourages code to be written in small steps, we’d have seen the exact cause immediately. Test-after is always tricky!

In conclusion, we think this is an interesting way of writing tests for your applications components. The visual feedback and the ability to use browser developer tools is a great benefit. We definitely will be using Cypress component testing in real projects in the future. However, due to longer execution time compared to regular unit testing we will keep writing the bulk of our tests as unit tests.