JS — Jest Unit testing Cheatsheet

Vinayak Hegde
7 min readDec 31, 2020

--

A quick overview to Jest, a test framework for Node.js. This guide targets Jest v23+.

Quick start

npm install --save-dev jest babel-jest/* Add to package.json */
"scripts": {
"test": "jest"
}
# Run your tests
npm test -- --watch

Optional flags

--coverageSee a summary of test coverage
--detectOpenHandlesSee a summary of ports that didn’t close
--runInBandRun all tests one after the other

See: Getting started

Setup and teardown

beforeEach(() => { ... })
afterEach(() => { ... })
beforeAll(() => { ... })
afterAll(() => { ... })

See: afterAll() and more

Writing tests

describe('My work', () => {
test('works', () => {
expect(2).toEqual(2)
})
it('works', () => {
···
})
})

Also under the alias: it(name, fn, timeout)

See: describe(), test(), expect()

Test structure

describe('makePoniesPink', () => {
beforeAll(() => {
/* Runs before all tests */
})
afterAll(() => {
/* Runs after all tests */
})
beforeEach(() => {
/* Runs before each test */
})
afterEach(() => {
/* Runs after each test */
})

test('make each pony pink', () => {
const actual = fn(['Alice', 'Bob', 'Eve'])
expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink Eve'])
})
})

React test renderer

import renderer from 'react-test-renderer'
import Link from './path-to-link-component'
it('works', () => {
const tree = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
).toJSON()
expect(tree).toMatchSnapshot()
})

React’s test renderer can be used for Jest snapshots. See: Snapshot test

Focusing tests

describe.only(···)
it.only(···) // alias: fit()

Also under the aliases: it.only(name, fn, timeout), and fit(name, fn, timeout)

See: test.only

Skipping tests

describe.skip(···)
it.skip(···) // alias: xit()and xtest()

Also under the aliases: it.skip(name, fn), xit(name, fn), and xtest(name, fn)

See: test.skip

Data-driven tests (Jest 23+)

Run the same test with different data:

test.each([[1, 1, 2], [1, 2, 3], [2, 1, 3]])('.add(%s, %s)', (a, b, expected) => {
expect(a + b).toBe(expected)
})

Or the same using template literals:

test.each`
a | b | expected
${1} | ${1} | ${2}
${1} | ${2} | ${3}
${2} | ${1} | ${3}
`('returns $expected when $a is added $b', ({ a, b, expected }) => {
expect(a + b).toBe(expected)
})

Or on describe level:

describe.each([['mobile'], ['tablet'], ['desktop']])('checkout flow on %s', (viewport) => {
test('displays success page', () => {
//
})
})

See: describe.each(), test.each(),

Jest Expect

Basic expectations

expect(value)
.not
.toBe(value)
.toEqual(value)
.toStrictEqual()

Note that toEqual is a deep equality check.

Example (Basic expectations)

expect(42).toBe(42) // Strict equality (===)
expect(42).not.toBe(3) // Strict equality (!==)
expect([1, 2]).toEqual([1, 2]) // Deep equality
expect({ a: undefined, b: 2 }).toEqual({ b: 2 }) // Deep equality
expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 })

See: expect()

Objects expectations

expect(value)
.toBeInstanceOf(Class)
.toMatchObject(object)
.toHaveProperty(keyPath, value)

Example (Objects expectations)

expect({ a: 1 }).toHaveProperty('a')
expect({ a: 1 }).toHaveProperty('a', 1)
expect({ a: { b: 1 } }).toHaveProperty('a.b')
expect({ a: 1, b: 2 }).toMatchObject({ a: 1 })
expect({ a: 1, b: 2 }).toMatchObject({
a: expect.any(Number),
b: expect.any(Number)
})
expect([{ a: 1 }, { b: 2 }]).toEqual([
expect.objectContaining({ a: expect.any(Number) }),
expect.anything()
])
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws

See: expect()

Arrays expectations

expect(value)
.toEqual(item)
.toContain(item)
.toContainEqual(item)
.toHaveLength(number)

Example (Arrays expectations)

expect([]).toEqual(expect.any(Array))
expect(['Alice', 'Bob', 'Eve']).toHaveLength(3)
expect(['Alice', 'Bob', 'Eve']).toContain('Alice')
expect([{ a: 1 }, { a: 2 }]).toContainEqual({ a: 1 })
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(['Alice', 'Bob']))

See: expect()

Strings expectations

expect(value)
.not
.toBe(value)
.toEqual(value)
.toStrictEqual()
.toMatch(regexpOrString)

Example (string expectations)

expect('long string').toMatch('str')
expect('string').toBe('string')
expect('string').toStrictEqual('string')
expect('string').toEqual(expect.any(String))
expect('coffee').toMatch(/ff/)
expect('pizza').not.toMatch('coffee')
expect(['pizza', 'coffee']).toEqual([expect.stringContaining('zz'), expect.stringMatching(/ff/)])

See: expect()

Snapshots expectations

expect(value)
.toMatchSnapshot()
.toMatchInlineSnapshot()

First run creates a snapshot. Subsequent runs match the saved snapshot. See: Snapshot testing.

Note that toMatchInlineSnapshot() requires Prettier to be set up for the project.

Example (Snapshots expectations)

expect(node).toMatchSnapshot()
// Jest 23+
expect(user).toMatchSnapshot({
date: expect.any(Date)
})
expect(user).toMatchInlineSnapshot()

See: Inline snapshots, expect()

Numbers expectations

expect(value)
.toBeCloseTo(number, numDigits)
.toBeGreaterThan(number)
.toBeGreaterThanOrEqual(number)
.toBeLessThan(number)
.toBeLessThanOrEqual(number)

Example (Numbers expectations)

expect(2).toBeGreaterThan(1)
expect(1).toBeGreaterThanOrEqual(1)
expect(1).toBeLessThan(2)
expect(1).toBeLessThanOrEqual(1)
expect(0.2 + 0.1).toBeCloseTo(0.3, 5)
expect(NaN).toEqual(expect.any(Number))

See: expect()

Exceptions expectations

expect(value)
.toThrow(error)
.toThrowErrorMatchingSnapshot()

Example (Exceptions expectations)

// const fn = () => { throw new Error('Out of cheese!') }
expect(fn).toThrow()
expect(fn).toThrow('Out of cheese')
expect(fn).toThrowErrorMatchingSnapshot()

See: expect()

Booleans expectations

expect(value)
.toBeFalsy()
.toBeNull()
.toBeTruthy()
.toBeUndefined()
.toBeDefined()

Example (booleans expectations)

// Matches anything that an if statement treats as true 
// (not false, 0, '', null, undefined, NaN)
expect('foo').toBeTruthy()
// Matches anything that an if statement treats as false
// (false, 0, '', null, undefined, NaN)
expect('').toBeFalsy()
// Matches only null
expect(null).toBeNull()
// Matches only undefined
expect(undefined).toBeUndefined()
// The opposite of toBeUndefined
expect(7).toBeDefined()
// Matches true or false
expect(true).toEqual(expect.any(Boolean))

See: expect()

Function Assertions

expect(fn)
.toHaveBeenCalled()
.toHaveBeenCalledTimes(number)
.toHaveBeenCalledWith(arg1, arg2, ...)
.toHaveBeenLastCalledWith(arg1, arg2, ...)
expect(fn)
.toHaveBeenCalledWith(expect.anything())
.toHaveBeenCalledWith(expect.any(constructor))
.toHaveBeenCalledWith(expect.arrayContaining([ values ]))
.toHaveBeenCalledWith(expect.objectContaining({ props }))
.toHaveBeenCalledWith(expect.stringContaining(string))
.toHaveBeenCalledWith(expect.stringMatching(regexp))

Example (Function/mock assertions)

// Function was called
expect(fn).toBeCalled()
// Function was *not* called
expect(fn).not.toBeCalled()
// Function was called only once
expect(fn).toHaveBeenCalledTimes(1)
// Any of calls was with these arguments
expect(fn).toBeCalledWith(arg1, arg2)
// Last call was with these arguments
expect(fn).toHaveBeenLastCalledWith(arg1, arg2)
// Nth call was with these arguments (Jest 23+)
expect(fn).toHaveBeenNthCalledWith(callNumber, args)
// Function was returned without throwing an error (Jest 23+)
expect(fn).toHaveReturnedTimes(2)
// Function returned a value (Jest 23+)
expect(fn).toHaveReturnedWith(value)
// Last function call returned a value (Jest 23+)
expect(fn).toHaveLastReturnedWith(value)
// Nth function call returned a value (Jest 23+)
expect(fn).toHaveNthReturnedWith(value)
// Multiple calls
expect(fn.mock.calls).toEqual([['first', 'call', 'args'], ['second', 'call', 'args']])
// fn.mock.calls[0][0] — the first argument of the first cal
lexpect(fn.mock.calls[0][0]).toBe(2)

See: expect()

Others expectations

expect.extend(matchers)
expect.any(constructor)
expect.anything()
expect.addSnapshotSerializer(serializer)
expect.assertions(1)

See: expect()

Timers

Write synchronous test for code that uses native timer functions (setTimeout, setInterval, clearTimeout, clearInterval).

jest.useFakeTimers()it('works', () => {
jest.runOnlyPendingTimers()
jest.runTimersToTime(1000)
jest.runAllTimers()
})

Example (Timers)

// Enable fake timers
jest.useFakeTimers()

test('kill the time', () => {
const callback = jest.fn()

// Run some code that uses setTimeout or setInterval
const actual = someFunctionThatUseTimers(callback)

// Fast-forward until all timers have been executed
jest.runAllTimers()

// Check the results synchronously
expect(callback).toHaveBeenCalledTimes(1)
})

Or adjust timers by time with advanceTimersByTime():

// Enable fake timers
jest.useFakeTimers()

test('kill the time', () => {
const callback = jest.fn()

// Run some code that uses setTimeout or setInterval
const actual = someFunctionThatUseTimers(callback)

// Fast-forward for 250 ms
jest.advanceTimersByTime(250)

// Check the results synchronously
expect(callback).toHaveBeenCalledTimes(1)
})

Use jest.runOnlyPendingTimers() for special cases.

Note: you should call jest.useFakeTimers() in your test case to use other fake timer methods.

See: Timer Mocks

Jest Mocks

Mock functions

// mock
const fn = jest.fn()
// mock with return value
const fn = jest.fn(n => n * n)
jest.fn().mockReturnValue('hello')
jest.fn().mockReturnValueOnce('hello')

Example (Mock functions)

test('call the callback', () => {
const callback = jest.fn()
fn(callback)
expect(callback).toBeCalled()
// Second argument of the first call
expect(callback.mock.calls[0][1].baz).toBe('pizza')

// Match the first and the last arguments
// but ignore the second argument
expect(callback)
.toHaveBeenLastCalledWith(
'meal',
expect.anything(),
'margarita'
);
})

You can also use snapshots:

test('call the callback', () => {
// mockName is available in Jest 22+
const callback = jest.fn().mockName('Unicorn')
fn(callback)
expect(callback).toMatchSnapshot()
// ->
// [MockFunction Unicorn] {
// "calls": Array [
// ...
})

Example (Returning, resolving and rejecting values)

Your mocks can return values:

const callback = jest.fn().mockReturnValue(true);
const callbackOnce = jest.fn().mockReturnValueOnce(true);

Or resolve values:

const promise = jest.fn().mockResolvedValue(true);
const promiseOnce = jest.fn().mockResolvedValueOnce(true);

They can even reject values:

const failedPromise = jest.fn().mockRejectedValue("Error");
const failedPromiseOnce = jest.fn().mockRejectedValueOnce("Error");

You can even combine these:

const callback = jest.fn()
.mockReturnValueOnce(false)
.mockReturnValue(true);
// ->
// call 1: false
// call 2+: true

See: Mock functions

Mock implementations

const fn = jest.fn()
.mockImplementationOnce(() => 1)
.mockImplementationOnce(() => 2)
fn() // → 1
fn() // → 2

Mock Instances and calls

const Fn = jest.fn()a = new Fn()
b = new Fn()
Fn.mock.instances
// → [a, b]
Fn(123)
Fn(456)
Fn.mock.calls.length // → 2
Fn.mock.calls[0][0] // → 123
Fn.mock.calls[1][0] // → 456

See: .mock property

Mock modules

When using babel-jest, calls to jest.mock will automatically be hoisted to the top of the code block. Use jest.doMock if you want to explicitly avoid this behavior.

// The original lodash/memoize should exist
jest.mock('lodash/memoize', () => a => a)
// The original lodash/memoize isn’t required
jest.mock('lodash/memoize', () => a => a, { virtual: true })

Accessing the original module when using mocks

jest.mock('fs')
// Mocked module
const fs = require('fs')

// Original modul
econst fs = require.requireActual('fs')

Mock modules using a mock file

Create a file like __mocks__/lodash/memoize.js

module.exports = a => a

Add to your test

jest.mock('lodash/memoize')

Mock object methods

const spy = jest.spyOn(console, 'log').mockImplementation(() => {})
expect(console.log.mock.calls)
.toEqual([['dope'], ['nope']])
spy.mockRestore()
const spy = jest.spyOn(ajax, 'request').mockImplementation(() => Promise.resolve({ success: true }))
expect(spy).toHaveBeenCalled()
spy.mockRestore()

Mock getters and setters (Jest 22.1.0+)

const location = {}
const getTitle = jest.spyOn(location, 'title', 'get')
.mockImplementation(() => 'pizza')
const setTitle = jest.spyOn(location, 'title', 'set')
.mockImplementation(() => {})

Mock getters and setters

const getTitle = jest.fn(() => 'pizza')
const setTitle = jest.fn()
const location = {}
Object.defineProperty(location, 'title', {
get: getTitle,
set: setTitle
})

Clearing and restoring mocks

For one mock:

fn.mockClear() // Clears mock usage date (fn.mock.calls, fn.mock.instances)
fn.mockReset() // Clears and removes any mocked return values or implementations
fn.mockRestore() // Resets and restores the initial implementation

Note: mockRestore works only with mocks created by jest.spyOn.

For all mocks:

jest.clearAllMocks()
jest.resetAllMocks()
jest.restoreAllMocks()

Asynchronous tests

It’s a good practice to specify a number of expected assertions in async tests, so the test will fail if your assertions weren’t called at all.

test('async test', () => {
// Exactly three assertions are called during a test
expect.assertions(3)
// OR At least one assertion is called during a test
expect.hasAssertions()

// Your async tests
})

Note that you can also do this per file, outside any describe and test

beforeEach(expect.hasAssertions)

This will verify the presense of at least one assertion per test case. It also plays nice with more specific expect.assertions(3) declarations.

async/await

test('async test', async () => {
expect.assertions(1)
const result = await runAsyncOperation()
expect(result).toBe(true)
})

Promises

Return a Promise from your test:

test('async test', () => {
expect.assertions(1)
return runAsyncOperation().then(result => {
expect(result).toBe(true)
})
})

done() callback

Wrap your assertions in try/catch block, otherwise Jest will ignore failures:

test('async test', done => {
expect.assertions(1)
runAsyncOperation()
setTimeout(() => {
try {
const result = getAsyncOperationResult()
expect(result).toBe(true)
done()
} catch (err) {
done.fail(err)
}
})
})
test('async test', async done => {
expect.assertions(1)
const result = await runAsyncOperation()
expect(result).toBe(true)
done()
})

See: Async tutorial

References

Please comment if I have missed anything, I will update the article.

Happy unit testing!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Vinayak Hegde
Vinayak Hegde

Written by Vinayak Hegde

Dad, Husband, Son, Brother, Coder (mostly JavaScript and python), micro-blogger

No responses yet

Write a response