Javascript Jest:如何正确模拟节点模块?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/51492704/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 04:50:44  来源:igfitidea点击:

Jest: How to correctly mock a node module?

javascriptreactjsunit-testingreact-nativejestjs

提问by J. Hesters

I want to mock the node_module 'React Native Keychain' in React Native with Jest.

我想用 Jest 模拟 React Native 中的 node_module 'React Native Keychain'。

Following the docsI created a folder called __mocks__and created a file called react-native-keychain.jsin it.

按照文档,我创建了一个名为的文件夹,__mocks__并在其中创建了一个文件react-native-keychain.js

Here is the code within the file:

这是文件中的代码:

export default jest.mock("react-native-keychain", () => {
  const token = "abcdefghijklmnopqrstuvwxyz0123456789";
  const credentials = {
    username: "session",
    password: token
  };
  return {
    setGenericPassword: jest.fn(
      (username, password) => new Promise((resolve, reject) => resolve(true)) // eslint-disable-line no-unused-vars
    ),
    getGenericPassword: jest.fn(() => new Promise((resolve, reject) => resolve(credentials))), // eslint-disable-line no-unused-vars
    resetGenericPassword: jest.fn(() => new Promise((resolve, reject) => resolve(true))) // eslint-disable-line no-unused-vars
  };
});

I then wrote my tests for the functions that use this library:

然后我为使用这个库的函数编写了我的测试:

import * as keyChainFunctions from "react-native-keychain";
import { setToken, getToken, clearToken } from "./secureStorage";

const token = "abcdefghijklmnopqrstuvwxyz0123456789";

    describe("set token", () => {
      it("saves the token in the keychain", () => {
        expect.assertions(1);
        return setToken(token).then(res => {
          console.log(res);
          console.log(keyChainFunctions);
          expect(keyChainFunctions.setGenericPassword).toHaveBeenCalledWith("session", token);
        });
      });
    });

The problem is, that the test fails. I get the error message:

问题是,测试失败。我收到错误消息:

 FAIL  app/api/secureStorage/secureStorage.test.js
  set token
    ? saves the token in the keychain (42ms)

  ● set token ? saves the token in the keychain

    expect(jest.fn())[.not].toHaveBeenCalledWith()

    jest.fn() value must be a mock function or spy.
    Received: undefined

      10 |       console.log(res);
      11 |       console.log(keyChainFunctions);
    > 12 |       expect(keyChainFunctions.setGenericPassword).toHaveBeenCalledWith("session", token);
         |                                                    ^
      13 |     });
      14 |   });
      15 | });

      at app/api/secureStorage/secureStorage.test.js:12:52
      at tryCallOne (node_modules/promise/lib/core.js:37:12)
      at node_modules/promise/lib/core.js:123:15
      at flush (node_modules/asap/raw.js:50:29)
 ● set token ? saves the token in the keychain

    expect.assertions(1)

    Expected one assertion to be called but received zero assertion calls.

       6 | describe("set token", () => {
       7 |   it("saves the token in the keychain", () => {
    >  8 |     expect.assertions(1);
         |            ^
       9 |     return setToken(token).then(res => {
      10 |       console.log(res);
      11 |       console.log(keyChainFunctions);

      at Object.<anonymous> (app/api/secureStorage/secureStorage.test.js:8:12)

And the console.log()yield:

console.log()产量:

console.log app/api/secureStorage/secureStorage.test.js:10
    true

  console.log app/api/secureStorage/secureStorage.test.js:11
    { default:
       { addMatchers: [Function: addMatchers],

         advanceTimersByTime: [Function: advanceTimersByTime],
         autoMockOff: [Function: disableAutomock],
         autoMockOn: [Function: enableAutomock],
         clearAllMocks: [Function: clearAllMocks],
         clearAllTimers: [Function: clearAllTimers],
         deepUnmock: [Function: deepUnmock],
         disableAutomock: [Function: disableAutomock],
         doMock: [Function: mock],
         dontMock: [Function: unmock],
         enableAutomock: [Function: enableAutomock],
         fn: [Function: bound fn],
         genMockFromModule: [Function: genMockFromModule],
         isMockFunction: [Function: isMockFunction],
         mock: [Function: mock],
         requireActual: [Function: bound requireModule],
         requireMock: [Function: bound requireMock],
         resetAllMocks: [Function: resetAllMocks],
         resetModuleRegistry: [Function: resetModules],
         resetModules: [Function: resetModules],
         restoreAllMocks: [Function: restoreAllMocks],
         retryTimes: [Function: retryTimes],
         runAllImmediates: [Function: runAllImmediates],
         runAllTicks: [Function: runAllTicks],
         runAllTimers: [Function: runAllTimers],
         runOnlyPendingTimers: [Function: runOnlyPendingTimers],
         runTimersToTime: [Function: runTimersToTime],
         setMock: [Function: setMock],
         setTimeout: [Function: setTimeout],
         spyOn: [Function: bound spyOn],
         unmock: [Function: unmock],
         useFakeTimers: [Function: useFakeTimers],
         useRealTimers: [Function: useRealTimers] } }

What this tells me: 1. Either the mock works (because true is returned correctly) or the actual setGenericPasswordfunction from React Native Keychain is being called. 2. For some reason, keychainfunctionsis defined, but as the jest.mockobject instead of the three mocked functions.

这告诉我什么: 1. 模拟工作(因为 true 正确返回)或setGenericPassword来自 React Native Keychain的实际函数正在被调用。2.由于某种原因,keychainfunctions被定义为jest.mock对象而不是三个mocked函数。

It feels like 1 and 2 contradict each other, if the mock works. What am I doing wrong? Why does this mock not work? How can these weird console.log()s and failed tests be explained?

如果模拟有效,感觉 1 和 2 相互矛盾。我究竟做错了什么?为什么这个模拟不起作用?如何console.log()解释这些奇怪的s 和失败的测试?

Bamsesuggested in the comments to just export objects. This workaround works. I would still be interested how to correctly do it / what I did wrong.

Bamse在评论中建议只导出对象。此解决方法有效。我仍然会对如何正确做/我做错了什么感兴趣。

const token = "abcdefghijklmnopqrstuvwxyz0123456789";
const credentials = {
  username: "session",
  password: token
};

export const setGenericPassword = jest.fn(
  (username, password) => new Promise((resolve, reject) => resolve(true)) // eslint-disable-line no-unused-vars
);

export const getGenericPassword = jest.fn(
  () => new Promise((resolve, reject) => resolve(credentials)) // eslint-disable-line no-unused-vars
);

export const resetGenericPassword = jest.fn(() => new Promise((resolve, reject) => resolve(true))); // eslint-disable-line no-unused-vars

采纳答案by bamse

You could try to use genMockFromModulein your test and mock only the method you need.

您可以尝试在测试中使用genMockFromModule并仅模拟您需要的方法。

Hope it helps

希望能帮助到你