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

  • 413