DEV Community

Alexey Pavlov
Alexey Pavlov

Posted on • Edited on

3 1

How to test your own React Components library

We are going to test the components library, that we have created in this article. We have the Button component for testing.

We will use React Testing Library. To install it, run this script

yarn add -D @testing-library/react
Enter fullscreen mode Exit fullscreen mode

If you use CSS files

As you may know from a previous article, we use a CSS file to add styles to our button, but Jest can't handle such files by default and tests will fail. To run tests, we need to mock CSS files first.

To do that, add a jest.config.js file to the root of your project and paste a code below, it says that all CSS files should be handled by identity-obj-proxy library

module.exports = {
  moduleNameMapper: {
    '\\.(css)$': 'identity-obj-proxy',
  },
};
Enter fullscreen mode Exit fullscreen mode

and run

yarn add -D identity-obj-proxy
Enter fullscreen mode Exit fullscreen mode

Well, now we can start writing tests

Writing tests

Firstly, we have to decide that exactly we want to test. Let's check if the button is rendered correctly in different states and if it is clickable/not clickable. So we create a template and comment that we don't need for now.

import React from 'react';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('default', () => {});
// test('success', () => {});
// test('disabled', () => {});
// test('should be clickable', () => {});
// test('should not be clickable if disabled', () => {});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

For the first test, we need to import a render function from React Testing Library. We will check if default button has a primary variant. There are few ways to do it. For the first one, we need a function of searching element in the screen, let's import an object screen, that contains the function we need.

import React from 'react';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/Button/Button';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('default', () => {
render(<Button onClick={console.log}>Click me</Button>);
const button = screen.getByText('Click me');
expect(button.classList.contains('button-primary')).toBeTruthy();
});
// test('success', () => {});
// test('disabled', () => {});
// test('should be clickable', () => {});
// test('should not be clickable if disabled', () => {});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

The next one is to test a success variant. Let's have a look at another way of implementing such test. We will find our button by querySelector function.

import React from 'react';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/Button/Button';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('success', () => {
const { container } = render(
<Button onClick={console.log} variant="success">
Click me
</Button>
);
const button = container.querySelector('button');
expect(button!.classList.contains('button-success')).toBeTruthy();
});
// test('disabled', () => {});
// test('should be clickable', () => {});
// test('should not be clickable if disabled', () => {});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

Snapshot test

For disabled button state, let's use a snapshot test - the third way of implementing this type of tests. For that, we need an asFragment function, that we get from render.

import React from 'react';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/Button/Button';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('disabled', () => {
const { asFragment } = render(
<Button onClick={console.log} isDisabled={true}>
Click me
</Button>
);
expect(asFragment()).toMatchInlineSnapshot();
});
// test('should be clickable', () => {});
// test('should not be clickable if disabled', () => {});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

After running the test, Jest will generate a snapshot and paste it to our test as a toMatchInlineSnapshot function argument:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/Button/Button';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('disabled', () => {
const { asFragment } = render(
<Button onClick={console.log} isDisabled={true}>
Click me
</Button>
);
expect(asFragment()).toMatchInlineSnapshot(`
<DocumentFragment>
<button
class="button button-primary"
disabled=""
>
Click me
</button>
</DocumentFragment>
`);
});
// test('should be clickable', () => {});
// test('should not be clickable if disabled', () => {});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

Next time we will run the test, Jest will compare a result of asFragment function with this snapshot. Every time we create a new snapshot, we need to check that it meets our expectations.

Move on, now we will check if button is clickable. For that, we will mock onClick function and pass it to our button, then, emulate on button click with fireEvent.click. In the end, check that click was done, and it was done 1 time.

For testing a disabled button state, we will do the same, but use expect().not.toHaveBeenCalled() to be sure that click wasn't done. See example below:

import React from 'react';
import { render, screen } from '@testing-library/react';
import { Button } from '../src/Button/Button';
describe('Button', () => {
describe('Should be rendered correctly', () => {
test('should be clickable', () => {
const onClick = jest.fn();
render(<Button onClick={onClick}>Click me</Button>);
fireEvent.click(screen.getByText('Click me'));
expect(onClick).toHaveBeenCalledTimes(1);
});
test('should not be clickable if disabled', () => {
const onClick = jest.fn();
render(
<Button onClick={onClick} isDisabled={true}>
Click me
</Button>
);
fireEvent.click(screen.getByText('Click me'));
expect(onClick).not.toHaveBeenCalled();
});
});
});
view raw Button.test.tsx hosted with ❤ by GitHub

I will run all tests one more time to be sure that all is fine

Image description

Good, I pushed it to my GitHub. You can find all tests here

In the next article, we will deploy storybook to GitHub pages.

Top comments (0)

Visualizing Promises and Async/Await 🤓

async await

☝️ Check out this all-time classic DEV post on visualizing Promises and Async/Await 🤓

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay