Шпаргалка по Jest с примерами кода

Шпаргалка по Jest (Stylesheet Jest)

Базовая структура теста

describe('Выбор цвета', () => {
  beforeAll(() => {
    /* Запускается перед всеми тестами */
  })
  afterAll(() => {
    /* Запускается после всех тестов */
  })
  beforeEach(() => {
    /* Запускается перед каждым тестом */
  })
  afterEach(() => {
    /* Запускается после каждого теста */
  })

  test('Выбираем цвет', () => {
    const actual = fn(['Alice', 'Bob', 'John'])
    expect(actual).toEqual(['Pink Alice', 'Pink Bob', 'Pink John'])
  })
})

Поиск совпадений

Использование поиска совпаденийофициальная документация

Базовый поиск совпадений

expect(42).toBe(42) // Строгое равенство (===)
expect(42).not.toBe(3) // Строгое неравенство (!==)
expect([1, 2]).toEqual([1, 2]) // Глубокое сравнение
expect({ a: undefined, b: 2 }).toEqual({ b: 2 }) // Глубокое сравнение
expect({ a: undefined, b: 2 }).not.toStrictEqual({ b: 2 }) // Строгое сравнение

Определение истинности

// Совпадает с истинными значениями
expect('foo').toBeTruthy()
// Совпадает с ложными значениями
expect('').toBeFalsy()
// Совпадает только с `null`
expect(null).toBeNull()
// Совпадает только с `undefined`
expect(undefined).toBeUndefined()
// Значение должно быть определено
expect(7).toBeDefined()
// Совпадает с `true` или `false`
expect(true).toEqual(expect.any(Boolean))

Числа

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))

Строки

expect('длинная строка').toMatch('стр')
expect('строка').toEqual(expect.any(String))
expect('кофе').toMatch(/ф/)
expect('пицца').not.toMatch('кофе')
expect(['пицца', 'кофе']).toEqual([expect.stringContaining('цц'), expect.stringMatching(/ф/)])

Массивы

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

Объекты

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()
])

Исключения

// const fn = () => { throw new Error('Упс!') }
expect(fn).toThrow()
expect(fn).toThrow('Упс')
expect(fn).toThrowErrorMatchingSnapshot()

Снимки

expect(node).toMatchSnapshot()
expect(user).toMatchSnapshot({
  date: expect.any(Date)
})
expect(user).toMatchInlineSnapshot()

Функция для создания «моков» (фикций)

// const fn = jest.fn()
// const fn = jest.fn().mockName('Единорог') - именованная фикция
expect(fn).toBeCalled() // Функция была вызвана
expect(fn).not.toBeCalled() // Функция *не была* вызвана
expect(fn).toHaveBeenCalledTimes(1) // Функция была вызвана один раз
expect(fn).toBeCalledWith(arg1, arg2) // Любой вызов функции сопровождался указанными аргументами
expect(fn).toHaveBeenLastCalledWith(arg1, arg2) // При последнем вызове функции, ей были переданы указанные аргументы
expect(fn).toHaveBeenNthCalledWith(args) // Определенный вызов функции сопровождался указанными аргументами
expect(fn).toHaveReturnedTimes(2) // Функция возвращает значения без ошибок
expect(fn).toHaveReturnedWith(value) // Функция возвращает указанное значение
expect(fn).toHaveLastReturnedWith(value) // Последний вызов функции вернул указанное значение
expect(fn).toHaveNthReturnedWith(value) // Определенный вызов функции вернул указанное значение
expect(fn.mock.calls).toEqual([['first', 'call', 'args'], ['second', 'call', 'args']]) // Несколько вызовов
expect(fn.mock.calls[0][0]).toBe(2) // fn.mock.calls[0][0] — первый аргумент первого вызова

«Алиасы» (синонимы)

  • toBeCalled → toHaveBeenCalled
  • toBeCalledWith → toHaveBeenCalledWith
  • lastCalledWith → toHaveBeenLastCalledWith
  • nthCalledWith → toHaveBeenNthCalledWith
  • toReturnTimes → toHaveReturnedTimes
  • toReturnWith → toHaveReturnedWith
  • lastReturnedWith → toHaveLastReturnedWith
  • nthReturnedWith → toHaveNthReturnedWith

Примеси

expect(new A()).toBeInstanceOf(A)
expect(() => {}).toEqual(expect.any(Function))
expect('пицца').toEqual(expect.anything())

Поиск совпадений с промисами

test('Разрешенным значением должен быть "лимон"', () => {
  expect.assertions(1)
  // Не забудьте добавить оператор `return`
  return expect(Promise.resolve('лимон')).resolves.toBe('лимон')
  return expect(Promise.reject('осьминог')).rejects.toBeDefined()
  return expect(Promise.reject(Error('пицца'))).rejects.toThrow()
})

Или с помощью async/await:

test('Разрешенным значением должен быть "лимон"', async () => {
  expect.assertions(2)
  await expect(Promise.resolve('лимон')).resolves.toBe('лимон')
  await expect(Promise.resolve('лимон')).resolves.not.toBe('осьминог')
})

Официальная документация

Асинхронные тесты

Смотрите больше примеров в официальной документации Jest.

Хорошей практикой считается определение количества ожидаемых утверждений (assertions) в асинхронных тестах, тест провалится, если утверждения не будут вызваны.

test('Асинхронный тест', () => {
  expect.assertions(3) // В процессе тестирования вызывается ровно три утверждения
  // или
  expect.hasAssertions() // В процессе тестирования вызывается по крайней мере одно утверждение

  // Далее следуют асинхронные тесты
})

Обратите внимание, что вы также можете делать это в файле, за пределами любых describe и test:

beforeEach(expect.hasAssertions)

Это обеспечит присутствие хотя бы одного утверждения в процессе тестирования. Это также подходит для случаев, когда ожидается конкретное число утверждений — expect.assertions(3).

async/await

test('Асинхронный тест', async () => {
  expect.assertions(1)
  const result = await runAsyncOperation()
  expect(result).toBe(true)
})

Промисы

test('Асинхронный тест', () => {
  expect.assertions(1)
  return runAsyncOperation().then(result => {
    expect(result).toBe(true)
  })
})

Коллбек done()

Утверждение должно быть обернуто в блок try/catch, иначе Jest будет игнорировать ошибки:

test('Асинхронный тест', done => {
  expect.assertions(1)
  runAsyncOperation()
  setTimeout(() => {
    try {
      const result = getAsyncOperationResult()
      expect(result).toBe(true)
      done()
    } catch (err) {
      done.fail(err)
    }
  })
})

Фикции

Функции для создания фикций

test('Вызов коллбека', () => {
  const callback = jest.fn()
  fn(callback)
  expect(callback).toBeCalled()
  expect(callback.mock.calls[0][1].baz).toBe('пицца') // Второй аргумент первого вызова
  // Отслеживаем первый и последний аргументы, но игнорируем второй
  expect(callback).toHaveBeenLastCalledWith('мясо', expect.anything(), 'маргарита');
})

Вы также можете использовать снимки:

test('Вызов коллбека', () => {
  const callback = jest.fn().mockName('Единорог')
  fn(callback)
  expect(callback).toMatchSnapshot()
  // ->
  // [MockFunction Единорог] {
  //   "calls": Array [
  // ...
})

И передавать реализацию в функцию jest.fn():

const callback = jest.fn(() => true)

Читать подробнее

Возвращение, разрешение и отклонение значений

Ваши фикции могут возвращать значения:

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

Или разрешать значения:

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

Они даже могут отклонять значения:

const failedPromise = jest.fn().mockRejectedValue("Роскосмос, у нас случилась оказия");
const failedPromiseOnce = jest.fn().mockRejectedValueOnce("Роскосмос, у нас случилась оказия");

Вы можете комбинировать названные подходы:

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

// ->
//  вызов 1: false
//  вызов 2+: true

Создание фиктивных модулей с помощью метода jest.mock()

jest.mock('lodash/memoize', () => a => a) // Должна присутствовать реальная функция lodash/memoize
jest.mock('lodash/memoize', () => a => a, { virtual: true }) // Реальная функция lodash/memoize может отсутствовать

Читать подробнее

Обратите внимание, при использовании babel-jest вызовы jest.mock() будут подниматься в начало блока кода. Используйте jest.doMock() для предотвращения подобного поведения.

Создание фиктивных модулей в отдельных файлах

  1. Создаем файл, например, __mocks__/lodash/memoize.js:
    module.exports = a => a
  2. Добавлем его в тест:
    jest.mock('lodash/memoize')

Читать подробнее

Методы объектов фикций

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()

Геттеры и сеттеры фикций

Новая версия:

const location = {}
const getTitle = jest.spyOn(location, 'title', 'get').mockImplementation(() => 'пицца')
const setTitle = jest.spyOn(location, 'title', 'set').mockImplementation(() => {})

Старая версия:

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

Очистка и восстановление фикций

Для одной фикции:

fn.mockClear() // Удаляет дату использования фикции (fn.mock.calls, fn.mock.instances)
fn.mockReset() // Удаляет любые возвращенные значения или реализации фикции
fn.mockRestore() // Сбрасывает и восстанавливает первоначальную реализацию

Обратите внимание: mockRestore() работает только применительно к фикциям, созданным с помощью jest.spyOn().

Для всех фикций:

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

Получение доступа к исходному модулю при использовании «моков»

jest.mock('fs')
const fs = require('fs') // Модуль с "моком"
const fs = require.requireActual('fs') // Исходный модуль

Фиктивные таймеры

Позволяет писать синхронные тесты для кода, в котором используются нативные таймеры (setTimeoutsetIntervalclearTimeoutclearInterval).

// Разрешаем использование фиктивных таймеров
jest.useFakeTimers()

test('Убить время', () => {
  const callback = jest.fn()

  // Запускаем код, в котором используются `setTimeout()` или `setInterval()`
  const actual = someFunctionThatUseTimers(callback)

  // Перематываем до выполнения всех таймеров
  jest.runAllTimers()

  // Синхронно проверяем результаты
  expect(callback).toHaveBeenCalledTimes(1)
})

Или настраиваем таймеры по времени с помощью advanceTimersByTime():

// Разрешаем использование фиктивных таймеров
jest.useFakeTimers()

test('Убить время', () => {
  const callback = jest.fn()

  //  Запускаем код, в котором используются `setTimeout()` или `setInterval()`
  const actual = someFunctionThatUseTimers(callback)

  // Перематываем на 250 мс
  jest.advanceTimersByTime(250)

  // Синхронно проверяем результаты
  expect(callback).toHaveBeenCalledTimes(1)
})

В особых случаях используется jest.runOnlyPendingTimers().

Обратите внимание: jest.useFakeTimers() следует вызывать только для использования других методов фиктивных таймеров.

Тестирование, основанное на данных

Запускаем одни и те же тесты с разными данными:

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

Или с помощью шаблонных литералов:

test.each`
  a    | b    | expected
${3} | ${2} | ${1}
${1} | ${2} | ${3}
${2} | ${1} | ${3}
`('Возвращает $expected при сложении $a и $b', ({ a, b, expected }) => {
  expect(a + b).toBe(expected)
})

Или на уровне describe:

describe.each([['mobile'], ['tablet'], ['desktop']])('проверка выполнения за %s', (viewport) => {
  test('отображение загруженной страницы', () => {
    //
  })
})

describe.each()test.each()

Пропуск тестов. Шпаргалка по Jest.

Не запускать указанные тесты:

describe.skip('makePoniesPink'...
tests.skip('сделать каждого пони розовым'...

Запускать только указанные тесты:

describe.only('makePoniesPink'...
tests.only('сделать каждого пони розовым'...

Тестирование модулей с побочными эффектами

Node.js и Jest will кэшируют запрашиваемые (require) модули. Для тестирования модулей с побочными эффектами необходимо очищать реестр модулей между тестами:

const modulePath = '../module-to-test'

afterEach(() => {
  jest.resetModules()
})

test('первый тест', () => {
  // Подготовка условия для первого теста
  const result = require(modulePath)
  expect(result).toMatchSnapshot()
})

test('второй тест', () => {
  // Подготовка условия для первого теста
  const fn = () => require(modulePath)
  expect(fn).toThrow()
})

Возможно вам будет интересно — Шпаргалка по React- ответы на вопросы 2020-2021