简体   繁体   English

Jest 中的 Mocking 类不调用相同的方法

[英]Mocking classes in Jest does not call the same method

I'm trying to mock a class that is being imported into my code with require and then testing if a method of that class is getting called.我正在尝试模拟一个 class ,该 class 正在使用require导入我的代码,然后测试该 class 的方法是否被调用。

I've created a sample setup where this issue can be replicated:我创建了一个示例设置,可以在其中复制此问题:

// user.js
class User {
  getName() {
    return "Han Solo"
  }
}

module.exports = User
// user-consumer.js
const User = require('./user')
const user = new User()

module.exports.getUserName = () => {
  // do things here
  return user.getName() 
}
// user.test.js
const userConsumer = require('./user-consumer')
const User = require('./user')
jest.mock('./user')

it('should mock', () => {
  const user = new User()
  jest.spyOn(user, 'getName')
  userConsumer.getUserName()
  expect(user.getName).toBeCalled()
})

The error I get is as follows:我得到的错误如下:

失败的测试

If I used ES6 syntax this would work as shown on jest's documentation: https://jestjs.io/docs/en/es6-class-mocks如果我使用 ES6 语法,这将在 jest 的文档中显示: https://jestjs.io/docs/en/es6-class-mocks

But I unfortunately can't use ES6 on this project as it would require a lot of refactoring.但不幸的是,我不能在这个项目上使用 ES6,因为它需要大量的重构。

I also tried mocking the class with the module factory parameter我还用模块出厂参数试过mocking class

jest.mock('./user', () => {
  return jest.fn(() => {
    return {
      getName: jest.fn(),
    }
  })
})

It still doesn't work.它仍然不起作用。 When I log console.log(user.getName) in user-consumer.js:5 it does show that the method has been mocked but whatever is called in user.getName() is not the consumer function still returns "Han Solo".当我在user-consumer.js:5中记录console.log(user.getName)时,它确实表明该方法已被模拟,但在user.getName()中调用的不是消费者 function 仍然返回“Han Solo”。

I've also tried it with and without jest.spyOn and it still returns the same error.我也尝试过使用和不jest.spyOn ,它仍然返回相同的错误。

Is this just not possible with none ES6 syntax?这在没有 ES6 语法的情况下是不可能的吗?

The problem is that Jest spies have undocumented behaviour.问题是 Jest 间谍有无证行为。

Even if prototype method is the same for all instances:即使所有实例的原型方法都相同:

new User().getName === new User().getName

A spy is specific to an instance:间谍特定于一个实例:

jest.spyOn(new User(), 'getName') !== jest.spyOn(new User(), 'getName') 

If a specific instance is unreachable, it's a prototype that needs to be spied:如果一个特定的实例是不可访问的,它是一个需要被窥探的原型:

jest.spyOn(User.prototype, 'getName')
userConsumer.getUserName()
expect(User.prototype.getName).toBeCalled();

A problem with jest.mock isn't specific to ES6 syntax. jest.mock的问题并非特定于 ES6 语法。 In order for a spy to be available for assertions and implementation changes, it should be exposed somewhere.为了让 spy 可用于断言和实现更改,它应该暴露在某个地方。 Declaring it outside jest.mock factory is not a good solution as it can often result in race condition described in the manual ;jest.mock工厂之外声明它不是一个好的解决方案,因为它通常会导致手册中描述的竞争条件; there will be one in this case too.在这种情况下也会有一个。 A more safe approach is to expose a reference as a part of module mock.一种更安全的方法是将引用公开为模块模拟的一部分。

It would be more straightforward for ES module because this way class export is kept separately: ES 模块会更直接,因为这样 class 导出是分开保存的:

import MockedUser, { mockGetName } from './user';

jest.mock('./user', () => {
  const mockGetName = jest.fn();
  return {
    __esModule: true,
    mockGetName,
    default: jest.fn(() => {
      return {
        getName: mockGetName
      }
    })
  }
})
...

For CommonJS module with class (function) export, it will be efficiently exposed as class static method:对于带有 class(函数)导出的 CommonJS 模块,它将被有效地暴露为 class static 方法:

import MockedUser from './user';

jest.mock('./user', () => {
  const mockGetName = jest.fn();
  return Object.assign(
    jest.fn(() => {
      return {
        getName: mockGetName
      }
    }),
    { mockGetName }
  })
})
...
MockedUser.mockGetName.mockImplementation(...);
userConsumer.getUserName()
expect(MockedUser.mockGetName).toBeCalled();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM