title: Jest: How to correctly mock a node module?
body: <p>I want to mock the node_module 'React Native Keychain' in React Native with Jest.</p>
  
  <p>Following the <a href="https://jestjs.io/docs/en/manual-mocks#mocking-node-modules" rel="noreferrer">docs</a> I created a folder called <code>__mocks__</code> and created a file called <code>react-native-keychain.js</code> in it.</p>
  
  <p>Here is the code within the file:</p>
  
  <pre><code>export default jest.mock("react-native-keychain", () =&gt; {
    const token = "abcdefghijklmnopqrstuvwxyz0123456789";
    const credentials = {
      username: "session",
      password: token
    };
    return {
      setGenericPassword: jest.fn(
        (username, password) =&gt; new Promise((resolve, reject) =&gt; resolve(true)) // eslint-disable-line no-unused-vars
      ),
      getGenericPassword: jest.fn(() =&gt; new Promise((resolve, reject) =&gt; resolve(credentials))), // eslint-disable-line no-unused-vars
      resetGenericPassword: jest.fn(() =&gt; new Promise((resolve, reject) =&gt; resolve(true))) // eslint-disable-line no-unused-vars
    };
  });
  </code></pre>
  
  <p>I then wrote my tests for the functions that use this library:</p>
  
  <pre><code>import * as keyChainFunctions from "react-native-keychain";
  import { setToken, getToken, clearToken } from "./secureStorage";
  
  const token = "abcdefghijklmnopqrstuvwxyz0123456789";
  
      describe("set token", () =&gt; {
        it("saves the token in the keychain", () =&gt; {
          expect.assertions(1);
          return setToken(token).then(res =&gt; {
            console.log(res);
            console.log(keyChainFunctions);
            expect(keyChainFunctions.setGenericPassword).toHaveBeenCalledWith("session", token);
          });
        });
      });
  </code></pre>
  
  <p>The problem is, that the test fails. I get the error message:</p>
  
  <pre><code> 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);
      &gt; 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", () =&gt; {
         7 |   it("saves the token in the keychain", () =&gt; {
      &gt;  8 |     expect.assertions(1);
           |            ^
         9 |     return setToken(token).then(res =&gt; {
        10 |       console.log(res);
        11 |       console.log(keyChainFunctions);
  
        at Object.&lt;anonymous&gt; (app/api/secureStorage/secureStorage.test.js:8:12)
  </code></pre>
  
  <p>And the <code>console.log()</code> yield:</p>
  
  <pre><code>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] } }
  </code></pre>
  
  <p>What this tells me:
   1. Either the mock works (because true is returned correctly) or the actual <code>setGenericPassword</code> function from React Native Keychain is being called.
   2. For some reason, <code>keychainfunctions</code> is defined, but as the <code>jest.mock</code> object instead of the three mocked functions.</p>
  
  <p>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 <code>console.log()</code>s and failed tests be explained?</p>
  
  <p><a href="https://stackoverflow.com/users/2890316/bamse">Bamse</a> suggested in the comments to just export objects. This workaround works. I would still be interested how to correctly do it / what I did wrong.</p>
  
  <pre><code>const token = "abcdefghijklmnopqrstuvwxyz0123456789";
  const credentials = {
    username: "session",
    password: token
  };
  
  export const setGenericPassword = jest.fn(
    (username, password) =&gt; new Promise((resolve, reject) =&gt; resolve(true)) // eslint-disable-line no-unused-vars
  );
  
  export const getGenericPassword = jest.fn(
    () =&gt; new Promise((resolve, reject) =&gt; resolve(credentials)) // eslint-disable-line no-unused-vars
  );
  
  export const resetGenericPassword = jest.fn(() =&gt; new Promise((resolve, reject) =&gt; resolve(true))); // eslint-disable-line no-unused-vars
  </code></pre>