开玩笑地进行存根功能

时间:2019-03-03 00:03:16

标签: javascript mocking jestjs

我了解这是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中,但以累加方式进行,因此添加数据不会删除以前存储的数据,也不会添加重复项。 / p>

无论如何,我的测试文件如下所示:文件名为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 :(得分:1)

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兼容版本。 (实际上,它更干净)。

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");
});