你应该几乎总是更喜欢not在本例中,模拟您应该测试的部分内容UserService
。为了说明原因,请考虑以下两个测试:
-
提供测试双重实现findByEmail
在回购对象上:
it("throws an error if the user already exists", async () => {
const email = "[email protected] /cdn-cgi/l/email-protection";
const user = { email, name: "Foo Barrington" };
const service = new UserService({
findByEmail: (_email) => Promise.resolve(_email === email ? user : null),
});
await expect(service.createUser(user)).rejects.toThrow("User already exists");
});
-
删除服务自己的服务getUserByEmail
method:
it("throws an error if the user already exists", async () => {
const email = "[email protected] /cdn-cgi/l/email-protection";
const user = { email, name: "Foo Barrington" };
const service = new UserService({});
service.getUserByEmail = (_email) => Promise.resolve(_email === email ? user : null);
await expect(service.createUser(user)).rejects.toThrow("User already exists");
});
对于您当前的实现,两者都很好。但让我们考虑一下事情可能会发生怎样的变化。
想象一下我们需要enrich用户模型getUserByEmail
在某个时刻提供:
async getUserByEmail(userEmail) {
const user = await this.userRepository.findByEmail(userEmail);
user.moreStuff = await this.userRepository.getSomething(user.id);
return user;
}
显然,我们不需要这些额外的数据来了解用户是否存在,因此我们排除了基本的用户对象检索:
async getUserByEmail(userEmail) {
const user = await this._getUser(userEmail);
user.moreStuff = await.this.userRepository.getSomething(user.id);
return user;
}
async createUser(userData) {
if (await this._getUser(userData.email)) {
throw new Error("User already exists");
}
return this.userRepository.create(userData);
}
async _getUser(userEmail) {
return this.userRepository.findByEmail(userEmail);
}
如果我们使用测试 1,我们根本不需要改变它-我们还在消费findByEmail
在回购协议中,内部实现发生变化的事实对于我们的测试来说是不透明的。但对于测试 2,即使代码仍然执行相同的操作,现在还是失败了。这是一个假阳性;功能有效,但测试失败。
事实上,您可以应用该重构,提取_getUser
,在新功能明确需求之前;事实是createUser
uses getUserByEmail
直接反映偶然重复this.userRepository.findByEmail(email)
- 他们有不同的改变理由。
或者想象我们做出一些改变breaks getUserByEmail
。让我们模拟一个丰富的问题,例如:
async getUserByEmail(userEmail) {
const user = await this.userRepository.findByEmail(userEmail);
throw new Error("lol whoops!");
return user;
}
如果我们使用测试 1,我们的测试createUser
也失败了,但就是这样correct结果!实现已损坏,无法创建用户。通过测试 2,我们有一个假阴性;测试通过,但功能不起作用。
在这种情况下,你可以说最好看看only getUserByEmail
失败了,因为这就是问题所在,但我认为当您查看代码时,这会非常令人困惑:"createUser
也调用该方法,但测试表明它没问题......”.