DEV Community

Cover image for Better testing with Storybook
Ryan Lanciaux
Ryan Lanciaux

Posted on • Originally published at ryanlanciaux.com

Better testing with Storybook

In this article, we will discuss strategy that I like to help keep Storybook stories up-to-date. For those that are unfamiliar, Storybook is a UI component workspace that I find helpful in building front-end applications.

It allows you to develop your components in isolation, interact with these components individually, and see quality issues at a much more granular level than the screen or page level. Additionally, this workspace serves as communication to other developers, noting the reusable components that exist in a system. Learn more about Storybook here.

One criticism I often hear is that it's easy for a Storybook project to get out-of-date when developers forget to add their components to Storybook. I often find that Storybook speeds up development but understand the tendency to build new components in the context of the live, running application. It's easy to forget about adding stories when you have an otherwise working and tested feature.

How do we keep this "building components in isolation" mindset top-of-mind in a way that provides immediate value to others may not necessarily develop their components in Storybook first? In this scenario, I often like to treat stories as part of my testing strategy.

Traditional Testing

Let's assume we are building an activity feed. On our activity feed item component, we want to ensure that we're rendering the correct text, and the button onClick event fires as expected. We could use react-testing-library to establish confidence that our component is working as anticipated.

We'll use the render utility to render the component that we wish to test. We'll check the text and onClick functionality to ensure that everything is working.

// ActivityFeedItem.js
export default function ActivityFeedItem({ name, text, onClick }) {
  return (
    <Card>
      <Heading>{name}</Heading>
      <Text>{text}</Text>
      <Button onClick={onClick}>See Details</Button>
    </Card>
  );
}

// ActivityFeedItem.test.js
import { render } from '@testing-library/react';
...
it("shows the correct text", () => {
  const { getByText } = render(
    <ActivityFeedItem
      name="This is the heading!"
      text="Nostrud tempor ullamco aute nostrud commodo cillum amet ad velit veniam officia minim."
    />
  );

  expect(
    getByText(
      "Nostrud tempor ullamco aute nostrud commodo cillum amet ad velit veniam officia minim."
    )
  ).toBeInTheDocument();
});
Enter fullscreen mode Exit fullscreen mode

When we run our tests, we'll see that all is working as expected.

Test Suites: 7 passed, 7 total
Tests:       9 passed, 9 total
Snapshots:   5 passed, 5 total
Time:        2.62s
Ran all test suites.
Enter fullscreen mode Exit fullscreen mode

Debugging failing tests

What happens if our test is failing, and we want to dive in to debug?

Test Suites: 1 failed, 6 passed, 7 total
Enter fullscreen mode Exit fullscreen mode

There are a couple of options but, one I use a lot is the debug utility from React testing library. This utility illuminates the HTML for the rendered element.

We could update our test as follows to leverage debug:

const { getByText, debug } = render(
  <ActivityFeedItem
    name="This is the heading!"
    text="Sit enim irure pariatur nostrud id non deserunt laboris veniam velit."
  />
)

debug()
Enter fullscreen mode Exit fullscreen mode

The debug utility will log the HTML for our components. This strategy would work well for our trivial example component, but on a more substantial component, this can get unwieldy pretty quickly.

Instead of defining our elements to render directly in our test, we can leverage Storybook stories for this. We'll use stories written in Storybook's component story format to serve as the element we wish to render in our test.

We'll create the story metadata first. This metadata provides information to Storybook about how we should display our stories within the utility. Next, we'll create a story with the component story format. You may notice that we're creating an arrow function, which is not unique to Storybook. We can export this arrow function and import it in our test.

// ActivityFeedItem.stories.js
export default { title: "ActivityFeedItem" }

export const standard = (callback = undefined) => {
  return (
    <ActivityFeedItem
      name="This is the heading"
      text="Nostrud tempor ullamco aute nostrud commodo cillum amet ad velit veniam officia minim."
      onClick={callback}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

Using the story in our test

Before where we rendered our component in the test, we'll use the imported story instead. Now, if we want to debug our test, we have a story we can use in addition to the other debugging strategies we may traditionally use.

import { standard } from "./ActivityFeedItem.stories"

it("shows the correct text", () => {
  const { getByText } = render(standard())

  expect(
    getByText(
      "Nostrud tempor ullamco aute nostrud commodo cillum amet ad velit veiam officia minim."
    )
  ).toBeInTheDocument()
})
Enter fullscreen mode Exit fullscreen mode

Storybook view of component

We now have a way to visualize and interact with the component we're testing.

Wrapping up

Storybook provides many benefits beyond testing, but sometimes it's easy to forget when we're trying to get features out the door. I've found that using Storybook as a tool to help ensure quality helps avoid this situation where stories become out-of-date or neglected.

Top comments (6)

Collapse
 
zacharythomasstone profile image
Zachary Stone • Edited

Excellent work! I had no idea that story book existed. But now I'm aware of what could be a weakness and how to get around that with a team. Thank you for sharing! Edit- I noticed you create music in your free time, did you happen to create the outro music in your video?

Collapse
 
ryanlanciaux profile image
Ryan Lanciaux

Thank you - I really appreciate you checking this out!

Music is a fun hobby - I did make the intro drone and outro clip on this. :D

Collapse
 
zacharythomasstone profile image
Zachary Stone

Nice!

I sent this article to my team lead, we are using Angular 8 right now on a project and are feeling the pangs of keeping up with components. Hopefully we can implement it in fairly quickly.

Collapse
 
sirseanofloxley profile image
Sean Allin Newell

I've been meaning to get a full design pipeline in place so a designer and coder can collab at high velocity, and I always knew storybook was the answer; I need to take some time to really dig into this.

Thanks for writing this up and sharing!

Collapse
 
ryanlanciaux profile image
Ryan Lanciaux

Thank you for reading through this! I LOVE Storybook and really find the workflow benefits it brings are massive. I have another post on the overall philosophy here ryanlanciaux.com/blog/2019/09/20/t... if that’s helpful. 👍👍

Collapse
 
arcticspacefox profile image
ArcticSpaceFox

Very cool tool. Maybe worth switching to 😁👍