A next-generation tool to create blazing-fast documentation sites
API
created:5/19/2021
updated:5/19/2021

Data-driven testing with react

Component driven development

An old/new concept nowadays is component-driven development (or CDD) and the broader functional programming. Both of those concepts are intended to encapsulate a component/function without side effects and use them as the basic building blocks of web applications.
This article focuses on components and their related assets, and a future article will go into documenting and testing pure functions.
For our development process, we recommend organizing each component in their own folder:
component foldercomponent assets in their folder
In the screenshot above, you can see a component VariantButton.tsx in its folder alongside its various associated files:
  • VariantButton.data.ts - some generated data-driven testing data.
  • VariantButton.docs.tsx - the component documentation (or stories).
  • VariantButton.test.ts - the generated jest snapshot and accessibility tests
  • the design-assets - this folder contains various assets associated with the component's documentation, such as images and markdown notes.
  • the __snapshots__ - this folder contains the saved output of the snapshot tests.

More testing with less effort

Adding tests for each of your components and the amount of work involved oftentimes will stop you short of achieving a high percentage of test coverage.
In this tutorial, we will show you step-by-step how to start a react component library and automatically create snapshot and accessibility tests and the associated data-driven testing tables of data.

The final projects

If you arent into reading the entire article to the end, you can jump straight to the final projects:
buildlivesource
gatsbylive appgithub
nextjslive appgithub
webpack 5live appgithub
webpack 4live appgithub

Getting started with gatsby

1. Initialize the project

mkdir react-data-testing && cd react-data-testing && yarn init -y

2. Add dependencies

yarn add gatsby react react-dom @component-controls/gatsby-theme-stories

3. Configure gatsby

create a gatsby-config.js file in your project's home directory:

gatsby-config.js

module.exports = {
plugins: [
{
resolve: '@component-controls/gatsby-theme-stories',
options: {
configPath: '.config',
},
},
],
};

4. Configure documentation site

create a folder .config and add a buildtime.ts configuration file inside it to specify the location of your documentation files:

.config/buildtime.ts

import { BuildConfiguration } from "@component-controls/core";
const config: BuildConfiguration = {
stories: ["../src/**/*.@(docs.tsx|mdx)"],
};
export default config;

5. Add scripts

package.json

"scripts": {
"build": "gatsby build",
"start": "gatsby develop"
},

6. Create your first component

Create a new folder src/components/VariantButton and add a file VariantButton.tsx inside it.
VariantButton.tsx

src/components/VariantButton/VariantButton.tsx

import React, { FC } from 'react';
type ButtonVariant =
| 'primary'
| 'accent'
| 'disabled'
| 'success'
| 'warning'
| 'error';
const variant_colors: Record<ButtonVariant, string> = {
primary: '#F2F2F2',
accent: '#F2F2F2',
disabled: '#828282',
success: '#F2F2F2',
warning: '#4F4F4F',
error: '#F2F2F2',
};
const variant_backgrounds: Record<ButtonVariant, string> = {
primary: '#2F80ED',
accent: '#9B51E0',
disabled: '#E0E0E0',
success: '#219653',
warning: '#F2C94C',
error: '#EB5757',
};
export interface VariantButtonProps {
/**
* button label text
*/
text: string;
/**
* variant kind
*/
variant?: ButtonVariant;
}
/**
* Button with variants
*/
export const VariantButton: FC<VariantButtonProps> = ({
text,
variant = 'primary',
}) => {
return (
<button
disabled={variant === 'disabled' ? true : undefined}
style={{
color: variant_colors[variant],
backgroundColor: variant_backgrounds[variant],
borderRadius: 8,
border: `1px solid #000000`,
display: 'flex',
alignItems: 'center',
}}
>
<div data-testid="label" style={{ padding: '5px 10px' }}>
{text}
</div>
</button>
);
};

7. Create a documentation (story) file for the component

VariantButton.docs.tsx

src/components/VariantButton/VariantButton.docs.tsx

import React from 'react';
import { Document, Example } from '@component-controls/core';
import { VariantButton, VariantButtonProps } from './VariantButton';
export default {
title: 'VariantButton',
component: VariantButton,
} as Document;
export const overview: Example<VariantButtonProps> = props => (
<VariantButton {...props} />
);
overview.controls = {
text: 'Button',
};

8. Further reading:

Component documentation page

you can now fire up your documentation site
yarn start
the following documentation page was automatically generated
component documentationcomponent documentation page
You can already see a lot of information on your new component that was automatically generated:
  1. Source code metrics (loc, comments, todos)
  2. Component description and package name
  3. Sample code for users to import your component
  4. A component playground, where you can see the source code
  5. Interactive props to play with your component and see the visual rendering
  6. Extenranl (and internal) dependecnies
  7. JSX rendering tree

Automated testing

Add cc-cli

the component-controls cc-cli package can be used to generate automatically tests and data for your react components
yarn add @component-controls/cc-cli --dev

Add jest packages

add jest and typescript packages, since we will create our tests with typescript for this tutorial.
yarn add ts-jest @types/jest @testing-library/react --dev

Configure jest and ts-jest

jest can be configured in the project's package.json or through a jest.config.js, or jest.config.ts files.

package.json

"jest": {
"preset": "ts-jest",
"globals": {
"ts-jest": {
"isolatedModules": true
}
}
}

Testing scripts

add scripts to generate the tests and run the tests

package.json

"scripts": {
...
"test:create": "cc-cli -w -g doc",
"test": "yarn jest"
},

Cofigure typescript

you will need a basic typescript configuration to run the tests with jest and ts-jest

tsconfig.json

{
"compilerOptions": {
"esModuleInterop": true,
"jsx": "react"
}
}

Create the tests

yarn test:create

Run the tests

this will execute the tests and create a snapshot file
yarn test

Customizing cc-cli templates

You can customize the cc-cli generated templates.
  1. Choose the target (cjs, esm, or ts)

package.json

"test:create": "cc-cli -w -g doc -f cjs",
  1. Choose test renderer (rtr, rtl, or enzyme)

package.json

"test:create": "cc-cli -w -g doc -r enzyme",
  1. Chose the template level

package.json

"test:create": "cc-cli -w -g store",

package.json

"test:create": "cc-cli -w -g story",
  1. Customize the generated tests
  • install @testing-library/jest-dom custom matchers
yarn add --dev @testing-library/jest-dom
  • replace the generic snapshot test with custom matchers

src/components/VariantButton/VariantButton.test.tsx

...
import '@testing-library/jest-dom';
...
it('custom test', () => {
const { getByTestId, container } = render(rendered);
expect(getByTestId("label")).toHaveTextContent("Button");
expect(container.children[0]).toHaveStyle("background: #2F80ED");
});

The tests UI

Starting the documentation site at this point should give you a set of test results:
component testscomponent tests page
You can notice the following test-related information
  1. Test summary (passed and failed), as well as the lowest coverage percentage
  2. A table with the snapshot and accessibility tests results
  3. A breakdown of the component files test coverage

Test coverage

From the coverage report, you can notice that only one of the branches was covered by our tests - in this case, we never tested for this condition disabled={variant === 'disabled' ? true : undefined}. We can correct this by adding a new example with the variant="disabled" property.

src/components/VariantButton/VariantButton.docs.tsx

export const disabled: Example = () => (
<VariantButton variant="disabled" text="Disabled" />
);
The tests will be re-run and the branch coverage increase to a full 100%

Test errors

The see the snapshot testing in effect, you can modify VariantButton component:

src/components/VariantButton/VariantButton.tsx

<div data-testid="label" style={{ padding: '15px 10px' }}>
{text}
</div>
The tests will be re-run automatically and the test page will now display an error to help you validate the introduced changes:
snapshot errorsnapshot error diff
To fix the tests, you can either delete the snapshot file (this will automatically re-run the test and generate a new snapshot) or revert the change.

Data-driven testing

Generate data

Data-driven testing is a testing method, where you create a single test and execute it multiple times by using a table that contains rows of data.
To create automatically data-driven testing data files, you can use cc-cli (with the -d option and specify the number of rows to generate):

package.json

"test:create": "cc-cli -w -g doc -d 5",
To achieve consistent data generation, you can specify the random generator seed with the -s parameter:

package.json

"test:create": "cc-cli -w -g doc -d 5 -s 1122334455",
you can now re-run the test:create script to generate the data and update the test file
yarn test:create

Review data

Starting the documentation site will display a table with the data rows in the testing page:
data-driven testingdata-driven testing UI

Manually edit the data

You can manually edit the data - for example, change the generated random values, or rename a row id.
edit dataedit data in your editor

Generate new random data

You can also use the randomize function in the controls table and then copy/paste the data into the data file.
generate random datagenerate random data