I always liked end-to-end testing and using a real browser to see the application run. I also love unit tests and probably used every testing framework out there to write the unit tests professionally. But what about component tests? Pieces of front-end code that live somewhere in between E2E and unit tests in the testing pyramid? In my opinion making E2E test runner "understand" components and run them as a "mini" web apps is much simpler than extending unit test synthetic environment. Since I snowboard, I know that going down is much simpler than going uphill!
Note this slide is from my presentation The Shape Of Testing Pyramid shown at AssertJS in 2018. The founders of Cypress.io Brian Mann and Randall Kent were sitting front row during the presentation.
If we could somehow avoid cy.visit(url)
and instead use cy.mount(<MyComponent withProp={...} />)
to "start" the component for framework X, then we could apply all Cypress command and interact with the component in the full browser just like a real user would. No more "scratchpad" dev pages showing individual custom Date components, etc. Just a browser running your component and the test runner clicking, asserting, spying, intercepting network requests, etc.
Here is an example component test from my blog post My Vision for Component Tests in Cypress.
1 | import React from "react" |
Notice how we can simply pass stubs and spies and it just works? I also made a mount
function for each major framework: React, Vue, Svelte, etc. Angular was difficult, but other Angular developers pushed it over the finish line and it worked.
I made the very first commit to my bahmutov/cypress-react-unit-test
repo on December 30, 2017. The first working React component test was made New Year's eve on Dec 31st, 2017. Yup, I was pretty excited about it.
Even more specific - the first commit was made almost at midnight at 11:48 PM EST. I should have waited for the strike of midnight clock, Cinderella-style.
The first test that worked happened next day Dec 31st, 2018
1 | import { HelloWorld } from '../../src/hello-world.jsx' |
Of course, it was "Hello World!"
The original component testing was a separate Cypress plugin. Good thing I love writing Cypress plugins. Later Dmitriy Kovalenko who worked at Cypress.io twice helped figure out how to mount the components in the same iframe as the specs, as opposed to the E2E tests that Cypress mounts in a separate iframe. This made things like stubs and spies work, thanks Dmitriy 🙏
Note: I worked a lot on Cypress component testing outside the normal business hours. When I left the company I transferred whatever repos they asked (cypress-react-unit-test
, cypress-vue-unit-test
, cypress-grep
, etc) to the Cypress organization. My plugins had code coverage built-in, stubbing of ES module imports, and other things that I thought were important to have. Since I left Cypress.io I have not worked on these plugins or any Cypress code really, except for reporting found issues.
Cypress company incorporated the component testing into the main test runner. You can read the official Cypress component testing documentation here.
The naming
I initially named these libraries "cypress-X-unit-test" because in principle you mount the component and interact with it like you would test a small piece of code. Only instead of arguments, you send user commands like clicks. Instead of the result you read the result from the page or other behaviors.
1 | // "normal" unit test |
And here is a unit React component test
1 | it('logs user in', () => { |
But I am not too attached to the naming. Everyone pretty much objected to the "unit test" part, and the official name became "Component Testing".
Access the component internal state
I always thought that interacting with the component through the DOM like the real user is the best testing strategy. But sometimes you need to access the internal state of the component. This is where I wrote bahmutov/cypress-react-app-actions but this works just as well with end-to-end tests that access a React we app.
Disclaimer
The timeline of events is how I remember and can reconstruct from the code and blog posts and presentations. If you think I am incorrect, please let me know and bring receipts.
Update 1: Brian Mann responds
Brian Mann responds that he and Chris talked about component testing and even tried it out in the test runner in 2016.
To better see, this is the included Slack message
Lovely. Except it is something they discussed and it never went anywhere. "The company greenlit the decision" is also weird to say. Of course, once I showed it again and again and again it was decided to make it part of the test runner. Again, everyone can see the 480 commits I made by myself with my best friend Renovate into the repo cypress-io/cypress-react-unit-test. Zero Brian, zero Chris until October 13, 2020.
What is really weird in Brian's response is the focus on "It was my idea, even discussed during funding". Never talked to me, I never saw those funding proposals. I would understand "Gleb, your code was bad, it was a prototype, we had to rewrite it all to make Component Testing the first-class feature". I would totally get it, no problem. But the current response... is just so weird for an open-source tool.
Update 2: npm info
Here is something else I realized: you can check out the author
of any NPM package either by looking online at package.json
file or by using npm info <package name> author
command. Let's take a look, shall we?
Learn more
Over these couple of years I tried to promote Cypress component testing a lot because I truly believe it is a good and powerful testing solution. Here are my blog posts, videos, presentations, and even courses that show why and how to do component testing.
- 📝 blog post My Vision for Component Tests in Cypress
- 📝 blog post React Native Web Component Testing Using Cypress And Vite
- 📝 blog post Split React Native Web Component Tests For Free
- 📝 blog post Cypress Component Testing Vs RTL and Jest and 📺 video
- 📝 blog post A Quick React Component Test
- 📝 blog post Component Code Coverage in Cypress v10
- 🎓 my course Testing The Swag Store
- 🖥️ presentation Learn Cypress React component testing by playing Sudoku
- 🖥️ presentation Test Angular/React/Vue/Svelte Components Without Fear
- 🖥️ presentation The Fuzzy Line Between End-to-End And Component Tests
- 🖥️ presentation Visual Testing for React Components
- 📺 video Verify Cypress Component Prop Calls Using Stubs, cypress-map, and cy-spok
- 📺 video Using Cypress Component Testing To Really Test A React Dark Mode Component
- 📺 video Setup GitHub Actions To Run Cypress Component Tests