jest.clearAllMocks vs jest.resetAllMocks vs jest.restoreAllMocks объяснено

Я знаю, что трудно запомнить и запутаться в том, что на самом деле делают эти clearAllMocks, resetAllMocks и restoreAllMocks и зачем нам это нужно?

Позвольте мне прояснить ваши мысли!

jest.clearAllMocks

Очищает все данные об использовании имитатора, такие как mock.calls, mock.instances, mock.contexts и mock.results, но не их реализацию.

Каждый раз, когда вызывается имитационная функция, она будет сохранять эти контексты использования в самом объекте имитационной функции.

Рассмотрим следующий пример. Мы собираемся протестировать getRandomNumber, которая фактически вызывает randomNumberGenerator() для получения случайного числа. Поэтому в данном случае мы будем издеваться над модулем random.service.

// main.test.js
jest.mock('random.service')

const { randomNumberGenerator } = require('random.service')

const getRandomNumber => randomNumberGenerator()

it('should return a number', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()

  console.log(randomNumberGenerator.mock) // 👈 check this out!

  expect(num).toBe(7)
})
Вход в полноэкранный режим Выход из полноэкранного режима

Как только вы вызовете функцию getRandomNumber, имитатор randomNumberGenerator также будет вызван соответствующим образом. После этого вы можете console.log(randomNumberGenerator.mock) и увидите что-то вроде этого.

{
  calls: [ [] ],
  contexts: [
    <ref *1> {...}
  ],
  instances: [
    <ref *1> {...}
  ],
  invocationCallOrder: [ 1 ],
  results: [ { type: 'return', value: 2 } ],
  lastCall: []
}
Вход в полноэкранный режим Выход из полноэкранного режима

Контексты будут полезны для некоторых утверждений, таких как ожидание количества вызовов, наличие у функции возврата и т.д. Это также может быть полезно для определенных случаев использования, когда эти контексты могут понадобиться для утверждений в перекрестных тестах.

Однако это также может привести к некоторым проблемам, если вы не очистите его. Пример ниже

it('example 1', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()
  expect(randomNumberGenerator).toBeCalledTimes(1)
})

it('example 2', () => {
  randomNumberGenerator.mockReturnValue(8)
  const num = getRandomNumber()
  expect(randomNumberGenerator).toBeCalledTimes(1) // This will failed!! because it expect 2 times.
})
Вход в полноэкранный режим Выйти из полноэкранного режима

Второй пример будет неудачным, и это потому, что mock.calls имеет 2 счета. Чтобы исправить это, вам нужно использовать jest.clearAllMocks в jest global как afterEach или beforeEach.

beforeEach(() => {
  jest.clearAllMocks()
})
Вход в полноэкранный режим Выйти из полноэкранного режима

Это указывает jest на очистку всех данных об использовании макета перед запуском следующего тестового случая.

jest.resetAllMocks

Суперсет clearAllMocks(), который также сбрасывает реализации имитационных функций с помощью совершенно нового jest.fn().

По умолчанию, все имитационные функции без реализации всегда будут возвращать undefined. А после добавления реализации она будет сохранять реализацию в объекте имитационной функции.

Следующий код все объяснит.

it('example 1', () => {
  randomNumberGenerator.mockReturnValue(7)
  const num = getRandomNumber()
  expect(num).toBe(7)
})

it('example 2', () => {

  // we didn't mock `randomNumberGenerator` in this test but this will return 7 because the last test case is added implementation.
  console.log(randomNumberGenerator())

  const num = getRandomNumber()
  expect(num).toBe(7)
})
Вход в полноэкранный режим Выход из полноэкранного режима

Это полезно, когда вам нужно повторно использовать имитационную функцию. Таким образом, вам не придется писать ее заново в каждом тестовом примере.

Если вы хотите, чтобы каждый тестовый пример был свежим, новым и автономным. Вы должны использовать его перед началом каждого тестового случая. Это может гарантировать отсутствие побочных эффектов для каждого тестового случая и позволит меньше беспокоиться.

jest.restoreAllMocks

Восстанавливает все макеты обратно к их первоначальной реализации, работает только для макетов, созданных с помощью jest.spyOn.

По какой-то причине вам нужно частично подружить модуль с определенной функцией. Классическим примером может служить тестирование времени, связанного с датой, как показано в следующем примере.

it('should be my birthday', () => {
  jest.spyOn(Date, 'now').mockReturnValue('2022-08-31T15:23:19.576Z')
  const result = isTodayMyBirthday()
  expect(result).toBe(true)
})
Вход в полноэкранный режим Выйти из полноэкранного режима

Это работает хорошо, но опасно, потому что spyOn как бы мутирует объект модуля, и если вы забыли восстановить все mocks, последующий тест будет использовать mock-версию Date.now(), а это не совсем очевидно и трудно отлаживать.

Как и в предыдущем решении, просто очищайте его перед каждым следующим тестовым случаем.

beforeEach(() => {
  jest.restoreAllMocks()
})
Вход в полноэкранный режим Выход из полноэкранного режима

Будьте осторожны, если вы увидите spyOn в вашем тестовом примере, это может быть причиной неудачного тестирования.

Заключение

Как вы видите, поведение имитационного API всегда сохраняет состояние, и вам придется очищать/сбрасывать/восстанавливать его явным образом. Если вы хотите, чтобы ваш тестовый пример всегда начинался с чистого листа и не имел побочных эффектов, можно поступить иначе. Вы можете сделать это в конфигурации jest.

// jest.config.js

const config = {
  clearMocks: true,
  resetMocks: true,
  restoreMocks: true
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Я надеюсь, что это поможет вам лучше понять. Ставьте лайк, единорог или спасибку, если это помогло! Спасибо.

Оцените статью
devanswers.ru
Добавить комментарий