如何在Jest中扩充模拟构造函数的实例

tif*_*fon 5 javascript unit-testing mocking jestjs

我想在Jest单元测试中扩充但不完全替换模拟构造函数的实例.

我想为实例添加一些值,但保持Jest的自动模拟优点.

例如:

A.js

module.exports = class A {
  constructor(value) {
    this.value = value;
  }
  getValue() {
    return this.value;
  }
}
Run Code Online (Sandbox Code Playgroud)

为了得到一些自动模拟真棒:

jest.mock('./A');
Run Code Online (Sandbox Code Playgroud)

使用automock,实例有一个模拟.getValue()方法,但它们没有.value属性.

模拟构造函数的文档化方法是:

// SomeClass.js
module.exports = class SomeClass {
  m(a, b) {}
}

// OtherModule.test.js
jest.mock('./SomeClass');  // this happens automatically with automocking
const SomeClass = require('./SomeClass')
const mMock = jest.fn()
SomeClass.mockImplementation(() => {
  return {
    m: mMock
  }
})

const some = new SomeClass()
some.m('a', 'b')
console.log('Calls to m: ', mMock.mock.calls)
Run Code Online (Sandbox Code Playgroud)

使用该方法A:

jest.mock('./A');

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

A.mockImplementation((value) => {
  return { value };
});

it('does stuff', () => {
  const a = new A();
  console.log(a); // -> A { value: 'value; }
});
Run Code Online (Sandbox Code Playgroud)

关于这一点的好处是你可以对返回的值做任何你想做的事,比如初始化.value.

缺点是:

  • 你没有免费获得任何自动插件,例如我需要将.getValue()自己添加到实例中
  • 您需要jest.fn()为每个创建的实例设置不同的mock函数,例如,如果我创建了两个实例A,每个实例都需要自己的jest.fn()模拟函数用于该.getValue()方法
  • SomeClass.mock.instances没有填充返回值(GitHub票证)

有一件事没有用(我希望也许Jest做了一些魔术):

A.mockImplementation((value) => {
  const rv = Object.create(A.prototype); // <- these are mocked methods
  rv.value = value;
  return rv;
});
Run Code Online (Sandbox Code Playgroud)

不幸的是,所有实例都使用相同的方法(正如人们所期望的那样,但值得一试).

我的下一步是通过检查原型(我猜)自己生成模拟,但我想看看是否有一个既定的方法.

提前致谢.

tif*_*fon 2

事实证明,这是固定的(从笑话 24.1.0 开始),并且问题中的代码可以按预期工作。


回顾一下,给定的类A

A.js

module.exports = class A {
  constructor(value) {
    this.value = value;
  }
  setValue(value) {
    this.value = value;
  }
}
Run Code Online (Sandbox Code Playgroud)

现在该测试将通过:

A.test.js

jest.mock('./A');

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

A.mockImplementation((value) => {
  const rv = Object.create(A.prototype); // <- these are mocked methods
  rv.value = value;
  return rv;
});

it('does stuff', () => {
  const a = new A('some-value');
  expect(A.mock.instances.length).toBe(1);
  expect(a instanceof A).toBe(true);
  expect(a).toEqual({ value: 'some-value' });
  a.setValue('another-value');
  expect(a.setValue.mock.calls.length).toBe(1);
  expect(a.setValue.mock.calls[0]).toEqual(['another-value']);
});
Run Code Online (Sandbox Code Playgroud)