maininformer
maininformer

Reputation: 1077

Unexpected behavior from Jest mockImplementation

I have code that I am writing tests for to refactor later, as such I cannot change the code or any of the dependencies. Here is the problem:

// foo.js
Knex = require('knex')

module.exports ={func}
// calling this inside func will 
// have the expected {a:4}
client = Knex()


async function func(){
  console.log(client)
  return true
}
// foo.spec.js
const foo = require('./foo')
const Knex = require('knex')
jest.mock('knex', ()=>jest.fn())

describe('jest.mockImplementation',()=>{
  it('should mock knex',async ()=>{
    Knex.mockImplementation(()=>({a:4}))
    // alternative, I can put
    // const foo = require('./foo')
    // here
    await foo.func()
  })
})
// jest.config.js
module.exports={
  "verbose": true,
  "testEnvironment": "node",
}
//package.json
{
  "dependencies": {
    "jest": "^26.6.3",
    "knex": "0.19.3"
  }
}

I run: $ jest --config jest.config.js --runInBand foo.spec.js and I expect there to be a console log of { a : 4}, but it is undefined. Note however, if I move the client inside func then it will log {a : 4}

alternatively, if I leave the client where it is and require foo in spec.js after mockImplementation, it will again have the expected console log.

I would have expected to see the correct behavior with the client being created outside of func and without needing to require foo after mockImplementation.

Why is this happening and how can I have the desired behavior without moving the client? also requireing inside the function is not the best.

I created this repl.it for experimentation; please don't update it for others' use:

https://replit.com/join/xmlwttzl-eminarakelian1

Upvotes: 0

Views: 211

Answers (1)

Lin Du
Lin Du

Reputation: 102617

The code of the module scope will be executed immediately when the module is required, so it is too late to provide the mock implementation in the test case.

jest.mock() will be hoisted to the top of the test file. It will be executed before the require statement, so when the module is required, the mock implementation provided in jest.mock() will be used.

Provide a mock implementation inside jest.mock() like this:

const foo = require('./foo');

jest.mock('knex', () => jest.fn(() => ({ a: 4 })));

describe('jest.mockImplementation', () => {
  it('should mock knex', async () => {
    await foo.func();
  });
});

test result:

 PASS  examples/66881537/foo.spec.js (6.347 s)
  jest.mockImplementation
    ✓ should mock knex (15 ms)

  console.log
    { a: 4 }

      at Object.<anonymous> (examples/66881537/foo.js:8:11)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        6.789 s

Upvotes: 1

Related Questions