jest.fn () утверждает, что не был вызван, но имеет - PullRequest
0 голосов
/ 26 февраля 2019

Я тестирую компонент Vue, который вызывает определенное действие в моем хранилище Vuex, когда в маршруте присутствует определенный параметр.Я высмеиваю действие с jest.fn().

Вот соответствующий код из компонента:

await this.$store.dispatch('someOtherAction');
if (this.$route.params && this.$route.params.id) {
    this.$store.dispatch('selection/selectElement', parseInt(this.$route.params.id, 10));
}

Вот смоделированная функция:

someOtherAction = jest.fn();
selectElement = jest.fn(() => console.log("selectElement has been called"));

Мой тест:

it('selects element if passed in route', async () => {
  const $route = {params: {id: '256'}};
  const wrapper = shallowMount(AbcModel, {
    mocks: {$route},
    store, localVue
  });
  expect(someOtherAction).toHaveBeenCalled();
  expect(selectElement).toHaveBeenCalled();
});

В выводе я вижу, что 'selectElement был вызван'.Ясно, что это было названо.И все же expect(selectElement).toHaveBeenCalled() терпит неудачу.

Как это возможно?Он отлично работает с другой функцией, которую я высмеял.Замена порядка, в котором я высмеиваю функции, не имеет значения.Устранение ожидания вызова другой функции также не имеет значения, поэтому не выглядит как столкновение.

1 Ответ

0 голосов
/ 27 февраля 2019

Как это возможно?

expect запускается и дает сбой до того, как selectElement сможет запустить.


Подробности

Очередь сообщений

JavaScript использует очередь сообщений .Текущее сообщение доходит до завершения до начала следующего.

Очередь PromiseJobs

ES6 представила Очередь PromiseJobs , котораяобрабатывает задания "которые являются ответами на урегулирование Обещания".Все задания в очереди PromiseJobs запускаются после завершения текущего сообщения и до начала следующего сообщения .

async / await

async иawait являются просто синтаксическим сахаром над обещаниями и генераторами .Вызов await для Promise по существу оборачивает остальную часть функции в обратный вызов, который должен быть запланирован в PromiseJobs, когда разрешается Promise.

Что происходит

Ваш тест запускается как текущее запущенное сообщение.Вызов shallowMount загружает ваш компонент, который работает до await this.$store.dispatch('someOtherAction');, который вызывает someOtherFunction, а затем, по существу, ставит в очередь остальную часть функции как Promise обратный вызов, который будет запланирован в очереди PromiseJobs, когда Promise разрешится.

Затем выполнение возвращается к тесту, который запускает два оператора expect.Первое проходит с момента вызова someOtherFunction, а второе - с ошибкой, поскольку selectElement еще не запущено.

Затем текущее запущенное сообщение завершается и выполняются отложенные задания в очереди PromiseJobs.Обратный вызов, который вызывает selectElement, находится в очереди, поэтому он запускается и вызывает selectElement, который регистрируется на консоли.


Решение

Убедитесь, чтоPromise обратный вызов, который вызывает selectElement, выполнялся перед запуском expect.

Когда бы ни было возможно, идеально было бы возвратить Promise, чтобы тест мог await его напрямую.

Если это невозможно, то обходной путь должен вызвать await для разрешенного Promise во время теста, который, по существу, ставит в очередь оставшуюся часть теста в конце очереди PromiseJobs и позволяет любым ожидающим обратным вызовам Promise запускаться первым:

it('selects element if passed in route', async () => {
  const $route = {params: {id: '256'}};
  const wrapper = shallowMount(AbcModel, {
    mocks: {$route},
    store, localVue
  });
  expect(someOtherFunction).toHaveBeenCalled();
  // Ideally await the Promise directly...
  // but if that isn't possible then calling await Promise.resolve()
  // queues the rest of the test at the back of PromiseJobs
  // allowing any pending callbacks to run first
  await Promise.resolve();
  expect(selectElement).toHaveBeenCalled();  // SUCCESS
});
...