Cypress VS Storybook for Component Testing Vue

In this post, I’m going to break down using Cypress and Storybook to test your components. They are both great tools, but offer slightly different experiences (and end results). Choosing which tool comes down to your end goal.

Here are a few questions to ask yourself:

  • Why are you implementing a component testing tool?
  • Do you use either tool already?
  • What will the learning curve be for your dev team?

We currently use Cypress for our e2e testing, and Storybook to house our UX design library and documentation. We wanted to carefully evaluate both options, as well as our motivations to pick the best tool.

TLDR - If your team uses Cypress already and your goal is to minimize the learning curve, use Cypress. It’s a great tool, and will allow you to start writing component tests quickly. If your goal is to build a robust and well-maintained Storybook library (or test speed is a concern) then choose Storybook.

Maybe Neither?

Our primary motivation was to find a reliable and robust component testing tool. The component testing landscape can be overwhelming. When looking at example code for many component tests it feels like it will be quite cumbersome to write simple, clean tests.

I don’t know about you, but I hate wasting time on tests that are brittle and hard to maintain.

We first took a look at Vue Testing Library. Vue recommends that you keep your tests high level and don’t try to test implementation details. With this in mind, I feel a non-visual testing tool makes it a lot harder to write good tests. If you can’t see what the component is doing, it seems a lot harder to debug. We decided to pass and look at Cypress VS Storybook.

Let’s dig in!

What does a test look like in each?

Cypress example

  import { mount } from 'cypress/vue'
  import { MyForm } from './MyForm'

  describe('MyForm', () => {
    it(‘submits the form’, () => {
      const onChangeSpy = cy.spy().as('onSubmitSpy')
      cy.mount(MyForm, { props: { onSubmit: onSubmitSpy } })
      cy.mount(MyForm)

      cy.get('#email').type('[email protected]');
      cy.get(‘#password').type('supersecret');
      cy.get('button').click()
      cy.get('@onSubmitSpy').should('have.been.called')
    })
  })

Storybook example

Keep in mind this code below also contains the basic setup for a story, so it's a bit longer.

import { expect } from '@storybook/jest'
import { userEvent, waitFor, within } from '@storybook/testing-library'
import { MyForm } from './MyForm'

export default {
  title: 'MyForm',
  component: MyForm,
  argTypes: {
    onSubmit: { action: true },
  },
}

const Template = (args) => <MyForm {...args} />
const Submitted = Template.bind({})
Submitted.play = async ({ args, canvasElement }) => {
  const canvas = within(canvasElement)
  await userEvent.type(canvas.getByTestId('email'), '[email protected]')
  await userEvent.type(canvas.getByTestId('password'), 'supersecret')
  await userEvent.click(canvas.getByRole('button'))
  await waitFor(() => expect(args.onSubmit).toHaveBeenCalled())
}

Cypress

Cypress has a slick visual GUI for both e2e and component testing. It’s been around for quite a while, and has a pretty established fan base.

Getting Started:
Writing your first component test, How to write component tests with Cypress

Pros

  • Less to Learn - Developers will already know the syntax if you are already using Cypress for e2e tests

  • Share code - You can share custom commands that you’ve already written for e2e tests

  • Tried and true - It’s recommended by the Vue core team

  • More mature - It’s older and more established than Storybooks Integration testing offering

Cons

  • Slower than storybook - Storybook uses Playwright under the hood, which can be much faster than Cypress

  • Two separate systems - Need to maintain component tests and stories separately. When integrated into storybook, it has the side effect of forcing developer to pay a lot more attention to creating robust stories and documentation.

  • Syntax is worse - Personally, I find the command queuing style of Cypress tests to be more complex and confusing. Who wants to nest a bunch of “promise-like commands” in 2022?

    Cypress bundles nine different third-party libraries into its tool, which creates a lot of inconsistency with the APIs.

    For example, the following two code snippets perform the same assertions:

      cy.get("#error-message").should("be.visible");
      cy.get("#error-message").should(($el) => expect($el).to.be.visible)
    

Storybook

Storybook is just starting to dabble in the component testing space, so the functionality is still under rapid development. In some ways, it makes a ton of sense to have your tests right there next to your component stories. Playwright is fast. It’s a strong and compelling offer from the folks over at Storybook.

Getting Started:
Interaction testing with Storybook, Component testing in Storybook with play functions

Pros

  • Really pushes Storybook - It forces you to build a robust and well-maintained Storybook Library. Often Storybook implementations can be neglected over time and fall out of date.

    This would help you commit to maintaining your stories because your tests depend on them

  • Faster than Cypress - It uses Playwright under the hood

  • Has better syntax - Playwrights’ async await VS Cypress command queuing

Cons

  • Newer kid on the block - Cypress component testing has been around longer

  • Another syntax to learn - It uses jest/playwright under the hood so if you use Cypress already, there is anther syntax to learn

  • Harder to configure CI

  • With Storybook you have to run an instance of Storybook to execute your tests against

    Note: This is supposed to be addressed in Storybook 6.5

  • Harder to mock/stub api - Cypress makes stubbing and mocking network requests a breeze with cy.intercept.

Conclusion

If you’re already deep in the game with Cypress for your e2e tests, it’s gonna be hard to justify using both. Cypress does a great job overall and is easy to integrate into your existing stack.

If you don’t yet use Cypress, then the Storybook offering becomes a lot more compelling. It’s fast, simple and clean to write your tests, and you have everything for your component library in one place. The biggest challenge here is if you decide to add e2e tests, you might still need Cypress. You could also consider Playwright here, which many people are moving to due to the speed, api and cross-browser compatibility.

It’s a wonderful world we live in to have such beautiful, well-designed and free tools to help us test our code. You really can’t go wrong with either one!