首页 > 解决方案 > 来自 Jest mockImplementation 的意外行为

问题描述

我有代码要编写测试以供以后重构,因此我无法更改代码或任何依赖项。这是问题所在:

// 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"
  }
}

我运行: $ jest --config jest.config.js --runInBand foo.spec.js并且我希望有一个控制台日志{ a : 4},但它是undefined. 但是请注意,如果我移动client内部,func那么它将记录{a : 4}

或者,如果我离开client它所在require foo的位置和spec.js之后mockImplementation,它将再次具有预期的控制台日志。

我本来希望看到client在外部创建的正确行为func,而不需要require fooafter mockImplementation

为什么会发生这种情况,我怎样才能在不移动的情况下获得所需的行为clientrequire函数里面也ing 不是最好的。

我创建了这个 repl.it 进行实验;请不要更新它以供他人使用:

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

标签: javascriptnode.jsjestjs

解决方案


模块作用域的代码会在需要模块时立即执行,所以在测试用例中提供模拟实现已经来不及了。

jest.mock()将被提升到测试文件的顶部。它将在require语句之前执行,因此当需要模块时,jest.mock()将使用中提供的模拟实现。

在内部提供一个模拟实现,jest.mock()如下所示:

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

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

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

测试结果:

 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

推荐阅读