我们假设我有以下课程:
export default class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
console.log(this.first + " " + this.last);
}
bla() {
return "bla";
}
}
假设我想创建一个模拟类,其中方法'sayMyName'将被模拟,方法'bla'将保持不变。
我写的测试是:
const Person = require("../Person");
jest.mock('../Person', () => {
return jest.fn().mockImplementation(() => {
return {sayMyName: () => {
return 'Hello'
}};
});
});
let person = new Person();
test('MyTest', () => {
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
})
第一个'expect'语句通过,这意味着'sayMyName'被成功模拟。但是,第二个'期望'因错误而失败:
TypeError:person.bla不是函数
据我所知,模拟类删除了所有方法。 我想知道如何模拟一个类,只有特定的方法才会被模拟。
答案 0 :(得分:14)
我没有看到模拟实现如何为您解决任何问题。我认为这更有意义
import Person from "./Person";
describe("Person", () => {
it("should...", () => {
const sayMyName = Person.prototype.sayMyName = jest.fn();
const person = new Person('guy', 'smiley');
const expected = {
first: 'guy',
last: 'smiley'
}
person.sayMyName();
expect(sayMyName).toHaveBeenCalledTimes(1);
expect(person).toEqual(expected);
});
});
答案 1 :(得分:7)
一直在问类似的问题,我想出了一个解决方案。无论实际在何处使用Person类实例,该方法都应该起作用。
const Person = require("../Person");
jest.mock("../Person", function () {
const { default: mockRealPerson } = jest.requireActual('../Person');
mockRealPerson.prototype.sayMyName = function () {
return "Hello";
}
return mockRealPerson
});
test('MyTest', () => {
const person = new Person();
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
答案 2 :(得分:3)
而不是模仿这个类,你可以像这样扩展它:
class MockedPerson extends Person {
sayMyName () {
return 'Hello'
}
}
// and then
let person = new MockedPerson();
答案 3 :(得分:3)
如果您使用的是Typescript,则可以执行以下操作:
Person.prototype.sayMyName = jest.fn().mockImplementationOnce(async () =>
await 'my name is dev'
);
在测试中,您可以执行以下操作:
const person = new Person();
const res = await person.sayMyName();
expect(res).toEqual('my name is dev');
希望这对某人有帮助!
答案 4 :(得分:3)
使用jest.spyOn()
是模拟单个方法并保留其余方法的正确的 Jest 方法。实际上,有两种稍微不同的方法。
1。仅在单个对象中修改方法
import Person from "./Person";
test('Modify only instance', () => {
let person = new Person('Lorem', 'Ipsum');
let spy = jest.spyOn(person, 'sayMyName').mockImplementation(() => 'Hello');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
// unnecessary in this case, putting it here just to illustrate how to "unmock" a method
spy.mockRestore();
});
2。修改类本身,以使所有实例受到影响
import Person from "./Person";
beforeAll(() => {
jest.spyOn(Person.prototype, 'sayMyName').mockImplementation(() => 'Hello');
});
afterAll(() => {
jest.restoreAllMocks();
});
test('Modify class', () => {
let person = new Person('Lorem', 'Ipsum');
expect(person.sayMyName()).toBe("Hello");
expect(person.bla()).toBe("bla");
});
为了完整起见,这是模拟静态方法的方式:
jest.spyOn(Person, 'myStaticMethod').mockImplementation(() => 'blah');
答案 5 :(得分:1)
我将@sesamechicken和@Billy Reilly的答案结合在一起,创建了一个util函数,该函数可模拟(一个或多个)类的特定方法,而不会对类本身造成影响。
/**
* @CrazySynthax class, a tiny bit updated to be able to easily test the mock.
*/
class Person {
constructor(first, last) {
this.first = first;
this.last = last;
}
sayMyName() {
return this.first + " " + this.last + this.yourGodDamnRight();
}
yourGodDamnRight() {
return ", you're god damn right";
}
}
/**
* Return a new class, with some specific methods mocked.
*
* We have to create a new class in order to avoid altering the prototype of the class itself, which would
* most likely impact other tests.
*
* @param Klass: The class to mock
* @param functionNames: A string or a list of functions names to mock.
* @returns {Class} a new class.
*/
export function mockSpecificMethods(Klass, functionNames) {
if (!Array.isArray(functionNames))
functionNames = [functionNames];
class MockedKlass extends Klass {
}
const functionNamesLenght = functionNames.length;
for (let index = 0; index < functionNamesLenght; ++index) {
let name = functionNames[index];
MockedKlass.prototype[name] = jest.fn();
};
return MockedKlass;
}
/**
* Making sure it works
*/
describe('Specific Mocked function', () => {
it('mocking sayMyName', () => {
const walter = new (mockSpecificMethods(Person, 'yourGodDamnRight'))('walter', 'white');
walter.yourGodDamnRight.mockReturnValue(", that's correct"); // yourGodDamnRight is now a classic jest mock;
expect(walter.sayMyName()).toBe("walter white, that's correct");
expect(walter.yourGodDamnRight.mock.calls.length).toBe(1);
// assert that Person is not impacted.
const saul = new Person('saul', 'goodman');
expect(saul.sayMyName()).toBe("saul goodman, you're god damn right");
});
});
答案 6 :(得分:0)
不能真正回答问题,但是我想展示一个用例,其中您要模拟一个依赖类来验证另一个类。
例如:Foo
取决于Bar
。在Foo
内部创建了Bar
的实例。您想模拟Bar
来测试Foo
。
酒吧班
class Bar {
public runBar(): string {
return 'Real bar';
}
}
export default Bar;
Foo类
import Bar from './Bar';
class Foo {
private bar: Bar;
constructor() {
this.bar = new Bar();
}
public runFoo(): string {
return 'real foo : ' + this.bar.runBar();
}
}
export default Foo;
测试:
import Foo from './Foo';
import Bar from './Bar';
jest.mock('./Bar');
describe('Foo', () => {
it('should return correct foo', () => {
// As Bar is already mocked,
// we just need to cast it to jest.Mock (for TypeScript) and mock whatever you want
(Bar.prototype.runBar as jest.Mock).mockReturnValue('Mocked bar');
const foo = new Foo();
expect(foo.runFoo()).toBe('real foo : Mocked bar');
});
});