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

Возьмем для примера эту функцию.

// kitchen.js
import { getIngredients } from './groceries'
import { blend } from './kitchen-utils'
export const cook = () => {
const ingredients = getIngredients()
return ingredients.length ?
         blend(ingredients) :
         'There was nothing to eat :((('
}

Давайте представим getIngrediants как функцию, которая извлекает данные из произвольного API. Вы, наверное, не хотите использовать API каждый раз, когда запускаете тест, не так ли? Это приведет к тому, что выполнение ваших модульных… тестов займет довольно много времени.

Итак, вы узнали, что есть вызов библиотеки Sinon.JS, который может спасти ваши задницы. И вы были счастливы, что на выполнение вашего теста уходит значительно меньше времени.

import * as groceries from './groceries'
// import other neccessary stuffs here
describe('cook', () => {
  describe('when there was no ingredient', () => {
    it('mama would be angry', () => {
      sinon.stub(groceries, 'getIngredients')
        .returns([])
      const food = cook()
      expect(food).toEqual('There was nothing to eat :(((')
    })
  })
})

Но $ h1t возникает, когда вы тестировали другую функцию в том же файле, где был определен cook

export const cook = () => { /* blabla */ }
export const serve = () => {
  const food = cook()
  return food.isDelicious ?
           food :
           'There was nothing to serve :((('
}

Потому что вы правильно cook заглушили (мысленно) и весь вечер выясняете, почему он не ведет себя так, как вы хотите.

import * as kitchen from 'kitchen'
describe('serve', () => {
  describe('when food is delicious, () => {
    it('should serve me that yummy food', () => {
      const mockFood = { isDelicious: true }
      sinon.stub(kitchen, 'cook').returns(mockFood)
      const food = serve()
      expect(food).toEqual(mockFood) // this ain't working
                                     // your test runs actual `cook`
                                     // implementation
    })
  })
})

Короче говоря, вы заглушали экспортированное значение cook, но реализация в serve вызывает cook напрямую, так что это будет преобразовано в функцию cook, которая была определена в kitchen.js.

Итак, как мы могли это обойти?

Мы могли бы изменить способ вызова cook, чтобы убедиться, что он сначала будет вызывать из экспортированного значения.

const cook = () => { /* blabla */ }
const serve = () => {
  const food = module.cook()
  // blabla
}
const module = {
  cook,
  serve
}
export module

Но я не хочу писать вот так экспорт, я так ленив, что хотел просто написать export const a = whatEver

Хорошо, все равно есть способ исправить это, сделав это

export const cook = () => { /* blabla */ }
export const serve = () => {
  const food = exports.cook()
  // blabla
}

СВЯТОЙ МОЛИЙ !!! Оно работает!!!

Ага-ага. Потому что exports будет относиться к области, в которую был экспортирован этот модуль. Итак, когда вы снова вставляете cook в kitchen, вместо этого будет использоваться метод заглушки.

Заявление об ограничении ответственности

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