I'm going to talk about unit tests here, not any other kind of tests. And unit tests have, in my opinion, the following list of criteria that they have to meet. This list makes it hard to write tests for code that use external libraries.
I've previously written a post about mocking moment.js for tests. Today, let’s talk about how to use mocks in a specific testing framework — Jest.
Jest, developed by Facebook, is based on another test library called Jasmine. This means it uses a widely familiar testing syntax, making it easy to adopt.
The main reason Facebook created Jest was to build a solid testing tool tailored for its UI framework — React. As a result, Jest includes specific methods suited for testing React components. However, this post will focus on Jest's broader use cases, not React-specific ones.
Let’s define our testing scenario. Suppose we have a class component we want to test (which is the focus of this post):
import React from 'react';
import { format } from 'date-fns';
import PropTypes from 'prop-types';
class DateComponent extends React.PureComponent {
render() {
const { timeStamp, formatStr } = this.props;
return <div>{format(timeStamp, formatStr)}</div>;
}
}
DateComponent.propTypes = {
timeStamp: PropTypes.number.isRequired,
formatStr: PropTypes.string,
};
DateComponent.defaultProps = {
formatStr: 'HH:mm:ss, YYYY-MM-DD',
};
Yes, this could be a functional component, but let’s stick with a class component for clarity.
So what do we have? DateComponent
is a simple component that takes two props: timeStamp
(required) and formatStr
(optional, with a default). The component renders the formatted timestamp using date-fns
. Straightforward enough.
In a previous post, we covered testing with Moment.js. Here, we’ll mock date-fns
. Here’s how:
// __mocks__/date-fns.js
export const format = (date, formatStr) => `${+date} [${formatStr}]`;
// __tests__/DateComponent.test.jsx
import renderer from 'react-test-renderer';
import DateComponent from '../DateComponent';
jest.mock('date-fns');
const timeStamp = 1540035262000;
describe('DateComponent', () => {
it('should render with default format', () => {
const tree = renderer
.create(<DateComponent timeStamp={timeStamp} />)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should render with custom format', () => {
const tree = renderer
.create(<DateComponent timeStamp={timeStamp} formatStr="YYYY/MM/DD" />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
Easy enough. Now, what if we want to use DateComponent
inside another component? We’ll likely want to mock it there too. Why? Because each unit test should focus on just one unit — the component under test — not its dependencies.
Consider this ViewComponent
, which uses DateComponent
:
// ViewComponent.jsx
import React from 'react';
import DateComponent from '../DateComponent';
const ViewComponent = () => {
const startTimestamp = 1540035262000;
const endTimestamp = 1540036000000;
return (
<>
<p>
Start date: <DateComponent timeStamp={startTimestamp} />
</p>
<p>
End date: <DateComponent timeStamp={endTimestamp} />
</p>
</>
);
};
export default ViewComponent;
If we test ViewComponent
without mocking DateComponent
, we’d be testing both — which is not ideal. We want our tests to stay focused.
To mock DateComponent
, add a mock file in the same directory under __mocks__
:
// __mocks__/DateComponent.jsx
import React from 'react';
const DateComponent = ({ timeStamp }) => (
<div data-mock="DateComponent">timeStamp: {timeStamp}</div>
);
export default DateComponent;
This mock omits all logic and simply displays the input props. It helps us verify how ViewComponent
uses DateComponent
without involving the actual implementation.
Now, let’s write a test for ViewComponent
:
// __tests__/ViewComponent.test.jsx
import renderer from 'react-test-renderer';
import ViewComponent from '../ViewComponent';
jest.mock('../DateComponent');
describe('ViewComponent', () => {
it('should render correctly', () => {
const tree = renderer.create(<ViewComponent />).toJSON();
expect(tree).toMatchSnapshot();
});
});
And that's it! You’ve now mocked a dependency using Jest and kept your unit tests focused and maintainable.
You might also be interested in the following posts:
I'm going to talk about unit tests here, not any other kind of tests. And unit tests have, in my opinion, the following list of criteria that they have to meet. This list makes it hard to write tests for code that use external libraries.