Jest
Jest is a common testing framework for JavaScript and TypeScript projects.
Configuration
TypeScript
You can run Jest tests written in TypeScript with either Babel or ts-jest
.
Using Babel
Install all of Babel's dependencies:
npm install -D babel-jest @babel/core @babel/preset-env @babel/preset-typescript
Using ts-jest
To set up ts-jest
:
# Install ts-jest
npm install -D jest typescript ts-jest @types/jest
# Init a config
npx ts-jest config:init
If the initial config doesn't work, try:
npx jest --init
Then set the preset
option to "ts-jest"
.
See Using TypeScript from the Jest documentation
Usage
Mocking
Spies
Mock functions (AKA spies) are functions with no implementation created by Jest for tracking usages. This is especially useful for testing code with good separation of responsibilities, as the implementation of the function is generally not required.
Take a very simple function:
function print(content, printer) {
if (!content) {
return;
}
printer.send(content);
}
Since this is not responsible for the printer itself, that part can be mocked entirely in tests.
describe('print', () => {
it('attempts to print when given some content', () => {
const mockPrinter = { send: jest.fn() };
print('content', mockPrinter);
expect(mockPrinter.send).toHaveBeenCalledWith('content');
});
it('does not print when given no content', () => {
const mockPrinter = jest.fn({ send: jest.fn() });
print(undefined, mockPrinter);
expect(mockPrinter.send).not.toHaveBeenCalled();
});
});
NOTE: In the example, there are two instances of mockPrinter
, one for each test. If there was only one, it would need to be cleared between tests.
See Clearing mocks for more info.
Clearing mocks
Usage information like calls and instances are kept per-mock from jest.fn()
. This means that between tests, mocks will hold information about previous tests, which is often undesirable behaviour.
describe('buy', () => {
const redirect = jest.fn();
it('redirects users to their cart', () => {
buy('banana', redirect);
expect(redirect).toHaveBeenCalledWith('cart');
});
it('does not redirect users when nothing happens', () => {
// Failure! redirect has already been called!
expect(redirect).not.toHaveBeenCalled();
});
});
There are several ways to clear mocks, but the most often used is jest.clearAllMocks()
. This is often used in conjunction with beforeEach()
, to clear mocks before running each test:
describe('buy', () => {
beforeEach(() => {
jest.clearAllMocks();
});
/* Use your imagination 🌈 */
});
If you find this is often required, there is an option to configure this in the wider jest configuration.
Most rarely, mocks can be cleared individually using .mockClear()
:
describe('queue', () => {
const callback = jest.fn();
const queue = new Queue('a', 'b', 'c');
it('dequeues and calls the callback', () => {
queue.dequeue(callback);
expect(callback).toHaveBeenCalledWith('c');
});
it('enqueues and calls the callback', () => {
callback.mockClear();
queue.enqueue('d', callback);
expect(callback).toHaveBeenCalledWith('d');
});
});
Mocking third party libraries
Mocking out third party libraries is often necessary when dealing with testing logic with consequences, like payment, authentication, etc.
This is generally done in one of two ways:
jest.mock()
- useful for if you don't need any original functionality, unit testsjest.spyOn()
- for when you do need the original functionality, integration tests