开玩笑如何模拟打字稿 class 但仅在 1 个测试中改变一些方法行为而不是在其他测试中修改模拟
Jest how to mock a typescript class but alternate some method behavior in only 1 test and not modify mock in others
我有打字稿classuserInfo.ts:
export class UserInfo {
public getName() {
return "I am real name";
}
}
我在 mocks 文件夹中有一个模拟的 class userInfo.ts:
export class UserInfo {
public getName() {
return "I am fake name";
}
}
我有一个客户:
import { UserInfo } from "./userInfo";
export class Client {
public functionToTest() {
let validation = new UserInfo();
return validation.getName();
}
}
最后我想对此进行两次测试,在第一个中我只想为这个测试覆盖 getName 模拟,在第二个中我想要模拟 class 行为所以:
import { Client } from "./client";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = UserInfo as jest.MockedClass<typeof UserInfo>; // I tried with this but with no success
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
// UserInfo.prototype.getName = jest.fn().mockImplementationOnce(() => {
// return "Something weird happened";
// });
userInfoMocked.prototype.getName = jest.fn().mockImplementationOnce(() => {
return "something weird happend";
});
// this is not working either
// Property 'getName' does not exist on type 'MockedClass<typeof UserInfo>'.
// userInfoMocked.getName = jest.fn().mockImplementationOnce(() => {
// return "something weird happend";
// });
let text = client.functionToTest();
expect(text).toBe('something weird happend');
let text2 = client.functionToTest();
expect(text2).toBe('I am fake name'); // I get undefined (I overwrote prototype!)
});
it('should get fake name now', () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe('I am fake name'); // I get undefined
});
});
我很惊讶这样一个常见的(我认为)功能无法实现?如何在这方面取得成功?这甚至可能吗?
您可以为模拟分配一个默认实现:
import userInfo from "./userInfo"
jest.mock("./userInfo", () => ({
getName: jest.fn(() => 'John Doe'),
}));
并且每次你想覆盖实现时:
userInfo.getName.mockImplementation(() => jest.fn().mockReturnValue('another value'));
如果您想在不同的测试中使用不同的模拟,请不要使用 mocks 文件夹。而是为每个测试创建您需要的模拟。 This 描述了您可以执行的不同类型的模拟。根据您的描述,我会使用 mockImplementation。例如,在一项测试中你可以做
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a real name'
}
}
}
在另一个测试中:
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a fake name'
}
}
}
所有方法都归结为同一件事,因此问题在于选择最适合您的代码结构且易于维护的方法。
这是我的解决方案:
它发生在不使用手动模拟时(因此在模拟下有 UserInfo.ts)它根本不起作用,但是当摆脱那个手动模拟并离开自动模拟时,我可以这样做:
import { mocked } from "ts-jest";
import { Client } from "./secretsManagerWrapper";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = mocked(UserInfo, false);
describe("Client", () => {
it.only("should get Name", () => {
let client = new Client();
userInfoMocked.mockImplementationOnce(function () {
return {
getName: () => {
return "John Doe Second";
},
};
});
成功了!
我还发现当我想使用手动模拟时(在 mocks 文件夹下)我可以使用 Spy:
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
jest.spyOn(userInfoMocked.prototype, "getName").mockImplementationOnce(() => {
return "John Doe Second";
});
let text = client.functionToTest();
expect(text).toBe("John Doe Second");
let text2 = client.functionToTest();
expect(text2).toBe("I am fake name"); // I get what I want now
});
it("should get fake name now", () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe("I am fake name"); // I get what I want now !
});
});
而且我可以完成我想要的:)
我有打字稿classuserInfo.ts:
export class UserInfo {
public getName() {
return "I am real name";
}
}
我在 mocks 文件夹中有一个模拟的 class userInfo.ts:
export class UserInfo {
public getName() {
return "I am fake name";
}
}
我有一个客户:
import { UserInfo } from "./userInfo";
export class Client {
public functionToTest() {
let validation = new UserInfo();
return validation.getName();
}
}
最后我想对此进行两次测试,在第一个中我只想为这个测试覆盖 getName 模拟,在第二个中我想要模拟 class 行为所以:
import { Client } from "./client";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = UserInfo as jest.MockedClass<typeof UserInfo>; // I tried with this but with no success
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
// UserInfo.prototype.getName = jest.fn().mockImplementationOnce(() => {
// return "Something weird happened";
// });
userInfoMocked.prototype.getName = jest.fn().mockImplementationOnce(() => {
return "something weird happend";
});
// this is not working either
// Property 'getName' does not exist on type 'MockedClass<typeof UserInfo>'.
// userInfoMocked.getName = jest.fn().mockImplementationOnce(() => {
// return "something weird happend";
// });
let text = client.functionToTest();
expect(text).toBe('something weird happend');
let text2 = client.functionToTest();
expect(text2).toBe('I am fake name'); // I get undefined (I overwrote prototype!)
});
it('should get fake name now', () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe('I am fake name'); // I get undefined
});
});
我很惊讶这样一个常见的(我认为)功能无法实现?如何在这方面取得成功?这甚至可能吗?
您可以为模拟分配一个默认实现:
import userInfo from "./userInfo"
jest.mock("./userInfo", () => ({
getName: jest.fn(() => 'John Doe'),
}));
并且每次你想覆盖实现时:
userInfo.getName.mockImplementation(() => jest.fn().mockReturnValue('another value'));
如果您想在不同的测试中使用不同的模拟,请不要使用 mocks 文件夹。而是为每个测试创建您需要的模拟。 This 描述了您可以执行的不同类型的模拟。根据您的描述,我会使用 mockImplementation。例如,在一项测试中你可以做
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a real name'
}
}
}
在另一个测试中:
UserInfo.mockImplementation(() => {
return {
getName: () => {
return 'I am a fake name'
}
}
}
所有方法都归结为同一件事,因此问题在于选择最适合您的代码结构且易于维护的方法。
这是我的解决方案: 它发生在不使用手动模拟时(因此在模拟下有 UserInfo.ts)它根本不起作用,但是当摆脱那个手动模拟并离开自动模拟时,我可以这样做:
import { mocked } from "ts-jest";
import { Client } from "./secretsManagerWrapper";
import { UserInfo } from "./userInfo";
jest.mock("./userInfo");
const userInfoMocked = mocked(UserInfo, false);
describe("Client", () => {
it.only("should get Name", () => {
let client = new Client();
userInfoMocked.mockImplementationOnce(function () {
return {
getName: () => {
return "John Doe Second";
},
};
});
成功了!
我还发现当我想使用手动模拟时(在 mocks 文件夹下)我可以使用 Spy:
describe("Client", () => {
it("should get Name", () => {
let client = new Client();
jest.spyOn(userInfoMocked.prototype, "getName").mockImplementationOnce(() => {
return "John Doe Second";
});
let text = client.functionToTest();
expect(text).toBe("John Doe Second");
let text2 = client.functionToTest();
expect(text2).toBe("I am fake name"); // I get what I want now
});
it("should get fake name now", () => {
let client = new Client();
let text3 = client.functionToTest();
expect(text3).toBe("I am fake name"); // I get what I want now !
});
});
而且我可以完成我想要的:)