Strategy & Success with
Adopting Cypress Component Testing
Murat K. Ozcan
Staff Engineer, Test Architect
Ambassador
Combinatorial Testing Comittee
CyCT will take front-end engineering
to the next level
How do we get buy-in?
Our experience at Extend
Jan 1st 2023
October 13th 2023
Contents
-
Learn-teach
-
The story
-
Adoption at your org
-
Survey results
Learn-Teach
Your efforts save many others
from having to do the same work
Become fluent
in your framework
Accumulate Examples
Make comparisons
CyCT vs ...
Contents
-
Learn-teach
-
The story
-
Adoption at your org
-
Survey results
The Extend Story
our setup
3 main apps, 80 eng
The Component Library
no tests (to speak of)
dozens of config options
The Component Library
The first "wow" moment
mocking...
Jest+RTL vs CyCT
CyCT is easy to
create, execute & maintain
Work with
The Early Adopters
the rest will follow
Contents
-
Learn-teach
-
The story
-
Adoption at your org
-
Survey results
Adoption At Your Org
Remember the Kano model
when introducing new features (CT)
Before CT, need e2e stable and fast
e2e
CT
Solve the e2e problems first
(our test performance was not great)
20s. startup
test duration
Use Custom mounts
abstract the right things
Wrapper Hell
import ProductsList from './ProductsList'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { store } from '../redux/store'
const storeWrappedMount =
(WrappedComponent: React.ReactNode, customStore = store, options = {}) =>
cy.mount(
<Provider store={customStore}>
<BrowserRouter>{WrappedComponent}</BrowserRouter>
</Provider>,
options
)
it('should...', () => {
storeWrappedMount(<ProductsList />)
...
})
import ProductDetails from './ProductDetails'
import { Provider } from 'react-redux'
import { MemoryRouter, Routes, Route } from 'react-router-dom'
import { store } from '../redux/store'
const routeWrappedMount = (
WrappedComponent: React.ReactNode,
route: string,
path: string,
customStore = store,
options = {}
) => {
window.history.pushState({}, '', route)
const wrapped = (
<Provider store={customStore}>
<MemoryRouter initialEntries={[route]}>
<Routes>
<Route element={WrappedComponent} path={path} />
</Routes>
</MemoryRouter>
</Provider>
)
return cy.mount(wrapped, options)
}
it('should ...', () => {
const route = `/${id}`
const path = '/:id'
routeWrappedMount(<ProductDetails />, route, path)
})
Mocking the easier way
and the better way
You can mock at a low level
but you rarely have to
Mocking the easier & better way
cy.intercept
(Mock Service Worker
in RTL)
our code
network requests
mock here
less here
+ custom mount
Remember the "wow" moment
higher confidence
lesser cost
(creation, execution, maintenance)
Custom mounts + intercept + Cy API =
Jest+RTL vs CyCT
Vite
"Maybe this is just our apps, the test run time is really quick, but would be good to cut some more time in the initial load."
CyCT uses your app's bundler
Webpack 4 can get slow at scale
At Extend, after 1k tests, our CT suite would load for 2 minutes upon initial start
The new tech benefits
the things that run in the browser
Vite vs Webpack for CyCT at scale
(2 minutes to 8 seconds initial start)
Support both Webpack and Vite
"cy:open-ct": "cypress open --component --browser chrome --config-file cypress/config/local.config.ts",
"cy:open-ct-vite": "cross-env BUNDLER=vite yarn cy:open-ct",
import { defineConfig } from 'cypress'
import { merge } from 'lodash'
import { mergeConfig } from 'vite'
import { CypressEsm } from '@cypress/vite-plugin-cypress-esm'
import viteConfig from '../../vite.config'
import webpackConfig from '../../webpack.config'
require('dotenv').config()
const webpackCyConfig = merge(webpackConfig, { ... })
const cyViteConfig = (): Record<string, any> =>
mergeConfig(viteConfig, {
plugins: [
CypressEsm({
ignoreModuleList: ['react', 'react-redux', 'redux-sagas', '**helmet**'],
// https://github.com/cypress-io/cypress/issues/27536
ignoreImportList: ['**/sagas**', '**helmet**', '**react-table**'],
}),
],
})
const devServer =
process.env.BUNDLER === 'vite'
? {
framework: 'react',
bundler: 'vite' as const,
viteConfig: cyViteConfig,
}
: {
framework: 'react',
bundler: 'webpack' as const,
webpackConfig: webpackCyConfig,
}
export default defineConfig({
component: {
experimentalSingleTabRunMode: true,
setupNodeEvents(on, config) {...},
devServer,
},
})
Support both Webpack and Vite
Contents
-
Learn-teach
-
The story
-
Adoption at your org
-
Survey results
Survey Results at
How often do you use CyCT?
How likely are you to recommend CyCT to other developers?
How would you describe your experience getting started with CyCT?
When do you typically write CyCT?
As DX we were promoting
these features when rolling out CyCT
Browser vs text in the terminal
Visual feedback lets you spot issues
you haven't written tests for
Is there anything in particular that you like about CyCT?
"The observability into the component in isolation is the game changer."
"Browser feedback, no more mocking RTK & Redux. Easier to mock the network."
"Easy to write and debug, and also the ability to view components while testing."
"Easy to test components without having to mock out almost everything."
"Custom mounts really help, abstracting the right things."
"Easy mocks, rendering the component in the browser."
"The wrapppers (custom mounts) make it very simple and straight forward to write cypress CT!"
"The visual component testing!"
"Simplicity of use"
"I like the visual feedback you get from component testing, and I also like that it is a lot faster testing individual components and scenarios where you don't necessarily need to test things E2E. I also like the modularity and how you can break out common logic to be used across tests."
"Visual feedback testing UI component just makes sense, very easy to use with testing components that has lots of user feedback like forms. It's also great to be able to mock actual network requests rather than mocking hooks so there is some coverage around that too."
"Very intuitive and easy to run/debug individual tests locally. Being able to see the DOM is a game changer. Commonly I'll work on components that may not be fully integrated into the app yet (or does not have an available API), and component tests let me build them out with the state & network requests and know exactly what they will look like."
"Having a UI to view and inspect for debugging failing tests. Not having to mock every single piece of redux individually for tests. Very simple to write tests for components in the middle of the tree. It is basically a mini integration test, which gives me more confidence in the test overall."
"Keep doing amazing work! I constantly recommend Cypress component tests to engineers at other companies."
Wrap up
Build expertise. Your efforts save many others from
having to do the same work.
Consider and explore CyCT. The feature-set, speed, simplicity,
make it a top contender for the future of component testing
Solve the e2e problems first.
Utilize custom mounts & network-level mocking
to abstract the right things, for low cost & high confidence
Adopting Cypress Component Testing In Your Organization
By Murat Ozcan
Adopting Cypress Component Testing In Your Organization
Adopting Cypress Component Testing In Your Organization
- 529