Cypress browser testing leveraging React Testing Library
If you're writing unit tests for your React components these days, there's a good
chance you're using React Testing Library.
It supports user-focused testing with reliable selectors to find elements, such as the data-testid
attribute or finding an element by role. Specific and reliable selectors are key to make sure tests are
resilient against unrelated changes to the component structure breaking existing unit tests.
For example, if we have this component with a data-testid
element:
1<CleanLink
2 to={`/tests/${publicId}/suite/${rowData.idx}/`}
3 data-testid={`test-suite-class-name-${rowData.idx}`}
4>
5 {rowData.name}
6</CleanLink>
Then we can get a reference to the specific element in a unit test using one of React Testing Library's
selectors such as getByTestId()
and then verify it contains the text content we expect:
1const { getByTestId } = render(
2 <TestSuiteList publicId="12345" testSuites={[testSuite]} />
3);
4
5expect(getByTestId("test-suite-class-name-1")).toHaveTextContent(
6 "my test suite"
7);
In addition to unit tests with React Testing Library, Cypress is a great testing library for writing full browser integration tests to verify the components of your application work together and deliver the end-user experience you are striving for. Is there a way we can leverage some of these same capabilities from React Testing Library in our Cypress tests?
Hand-rolled approach
A few years ago when I first started using React Testing Library and Cypress, I was creating all these data-testid
attributes for my unit tests.
Since I'd done all the work to create these data-testid
attributes for all the key components,
so I wanted to be able to re-use those attributes to select elements in my Cypress browser tests as well.
I found that Cypress had the ability to augment with my own custom commands, so I wrote
a custom command that replicated the getByTestId()
selector in Cypress:
1Cypress.Commands.add("getByTestId", testId =>
2 cy.get(`[data-testid="${testId}"]`)
3);
And over time I was replicating more and more of the testing library selectors
in my own code - such as getByRole()
:
1Cypress.Commands.add("getByRole", role =>
2 cy.get(`[role="${role}"]`)
3);
This was fine for a while, but I'm opportunistic in how I spend my time (lazy?) and didn't want to re-invent the whole testing library myself in Cypress commands. Thankfully other folks had the same thoughts and created a Cypress Testing Library a couple of years ago. That library brings many of the same capabilities from React Testing Library into the Cypress browser testing world. Now I can migrate my Cypress tests over to using that library and get rid of my own copies of all these selectors.
Getting started with Cypress Testing Library
Adding the Cypress Testing Library into my project turned out to be pretty easy.
First, install @testing-library/cypress
and cypress
(if you don't have Cypress installed already).
With yarn
:
1yarn add -D cypress
2yarn add -D @testing-library/cypress
Or with npm
:
1npm install --save-dev cypress
2npm install --save-dev @testing-library/cypress
Lastly, import the Cypress Testing Library commands into Cypress in cypress/support/index.js
1import "@testing-library/cypress/add-commands";
Now we're all set to use the testing library selectors in our Cypress tests!
Selectors in Cypress tests
Now we can use selectors like findByTestId()
in Cypress tests just as we would in unit tests:
1cy.findByTestId("test-suite-class-name-1").should(
2 "contain",
3 "projektor.example.spock.PassingSpec"
4);
Or findByRole()
:
1cy.findByRole(`dot-lineValue-${publicId}`).click();
Conclusion
Cypress Testing Library makes it easy to leverage the same selectors types of selectors you use for unit testing in your Cypress browser tests as well.
Happy testing!