Функция заглушки с шуткой - PullRequest
0 голосов
/ 03 марта 2019

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

Я пытаюсь протестировать функцию в Javascript, которая выполняет несколько операций с использованием других модулей, а также localStorage, и я хотел бы заглушить другие модули и вызов localStorage.Документация, которую я нашел для Jest, выглядела слишком упрощенной, чтобы я мог адаптироваться к своему варианту использования, например, объявить mock и затем вызвать его внутри теста - в моем случае это не происходит, так как функция, которую я хочу смоделироватьвызывается изнутри моей функцией, я не передаю ее как зависимость.Позвольте мне дать некоторый код для объяснения: имя файла dataInLocalStorage.js

import serialize from './serialize'; // simple module that serialises data
import deserialize from './deserialize'; // simple module that deserialises data
import findObject from './findObject'; // find an object in the deserialised data

const addDataToLocalStorage = (data) => {
  const dataStored = deserialize(localStorage.getItem('data')); // fetch data from localStorage
  const isStored = !!findObject(dataStored, data); // check whether the data I want to store is already there

  if (isStored) { return null; } // if data is already stored, skip

  const serializedData = serialize(data); // serialise data to be stored

  return localStorage.setItem('data', serializedData); // store serialised data in localStorage
};

export { addDataToLocalStorage };

Цель этого модуля - просто хранить данные в localStorage в последовательном порядке, но аддитивным образом, чтобыдобавление данных не удаляет ранее сохраненные данные, и дубликаты также не добавляются.

В любом случае, мой тестовый файл выглядит так: имя файла dataInLocalStorage.test.js

import { addDataToLocalStorage } from '../dataInLocalStorage';

describe('addDataToLocalStorage', () => {
  const deserialize = jest.fn();

  beforeAll(() => {
    localStorage.removeItem('data');
  });

  const data = {
    name: 'johnny'
  };

  addDataToLocalStorage(data);

  it('adds the data to local storage', () => {
    expect(deserialize).toHaveBeenCalled();
  });
});

Вотдовольно неожиданная ошибка для этой попытки.

expect(jest.fn()).toHaveBeenCalled()

Expected mock function to have been called, but it was not called.

      17 |
      18 |   it('adds the data to local storage', () => {
    > 19 |     expect(deserialize).toHaveBeenCalled();
         |                         ^
      20 |   });
      21 | });

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

Обратите внимание, что это не мой код на 100%, я изменил его для простоты, чтобы вам было легче читать, извините, если есть небольшие несоответствия, я старался изо всех сил старатьсявозможно при конвертации.

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


Примечание : Я пришел из Ruby on Rails, где издеваться над RSpec довольно просто, и я надеялся, что с Jest это будет так же просто.Это, вероятно, так, но я не могу обернуть это вокруг головы, так как не представляется возможным сделать прямую ссылку на функцию / модуль, который я хочу смоделировать.В RSpec выполнение allow(MySerializer).to receive(:call).and_return(...) дало бы результат, и мне не пришлось бы беспокоиться о том, что этот модуль вызывается во время теста.

1 Ответ

0 голосов
/ 03 марта 2019

Когда вы устанавливаете значение deserialize в шутку, вы изменяете значение переменной, а не устанавливаете ссылку, которую использует ваш код.Чтобы сохранить его в качестве ссылки, оно должно быть значением в объекте.

Для импорта объекта вы можете использовать import * as deserialize from "./deserialize";.Затем вы можете установить макет для ссылки с помощью deserialize.default = jest.fn().

https://codesandbox.io/s/88wlzp6q88

import { useIt } from "./use-default-export";

import * as myfunc from "./default-export-function";

test("use-default-export-function", () => {
  expect(useIt()).toEqual("real");
});

test("use-default-export-function with mock", () => {
  myfunc.default = jest.fn(() => "unreal");
  expect(useIt()).toEqual("unreal");
});

, в вашем тесте это будет ..

import { addDataToLocalStorage } from '../dataInLocalStorage';
import * as deserialize from './deserialize';
...
deserialize.default = jest.fn();

альтернативная версия TS Compat .. (которая на самом деле чище со всех сторон ..)

import { useIt } from "./use-default-export";

import myfunc from "./default-export-function";

jest.mock("./default-export-function", () => jest.fn());

test("use-default-export-function with mock", () => {
  useIt();
  expect(myfunc).toHaveBeenCalled();
});

возвращать / разрешать различные значения за тест (необходимо привести к jest.Mock, чтобы можно было использовать jest.fn() функции)

test("use-default-export-function with mock", () => {
  const aFunc = myfunc as jest.Mock;
  aFunc.mockResolvedValue("bar");
  useIt();
  expect(useIt()).resolves.toEqual("bar");
});

test("use-default-export-function with mock 2", () => {
  const aFunc = myfunc as jest.Mock;
  aFunc.mockReturnValue("foo");
  useIt();
  expect(useIt()).toEqual("foo");
});
...