我一直在阅读一些文章,以及Stack Overflow上的帖子,关于何时应该模拟函数,何时不应该,但我有一个案例,我不确定该怎么做。
我有一个UserService类,它使用依赖注入概念通过其构造函数接收依赖项。
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async getUserByEmail(userEmail) {
// would perform some validations to check if the value is an e-mail
const user = await this.userRepository.findByEmail(email);
return user;
}
async createUser(userData) {
const isEmailInUse = await this.getUserByEmail(userData.email);
if(isEmailInUse) {
return "error";
}
const user = await this.userRepository.create(userData);
return user;
}
}
我想测试createUser方法是否正常工作,为了进行测试,我创建了一个伪userRepository,它基本上是一个带有模拟方法的对象,我将在示例化UserService类时使用这些方法
const UserService = require('./UserService.js');
describe("User Service tests", () => {
let userService;
let userRepository;
beforeEach(() => {
userRepository = {
findOne: jest.fn(),
create: jest.fn(),
}
userService = new UserService(userRepository);
});
afterEach(() => {
resetAllMocks();
});
describe("createUser", () => {
it("should be able to create a new user", async () => {
const newUserData = { name: 'User', email: 'user@test.com.br' }
const user = { id: 1, name: 'User', email: 'user@test.com.br' }
userRepository.create.mockResolvedValue(user);
const result = await userService.createUser();
expect(result).toStrictEqual(user);
})
})
})
请注意,在createUser方法中,有一个对getUserByEmail方法的调用,该方法也是UserService类的方法,这就是我感到困惑的地方。
即使getUserByEmail方法是我正在测试的类的一个方法,我是否应该模拟它?如果这不是正确的方法,我该怎么办?
2条答案
按热度按时间bprjcwpo1#
你应该总是倾向于not来模拟你应该测试的部分,在这个例子中是
UserService
。为了说明原因,考虑以下两个测试:1.在repo对象上提供
findByEmail
的测试double实现:1.删除服务自己的
getUserByEmail
方法:对于您当前的实现,这两个都可以通过。但让我们想想事情可能会如何改变。
想象一下,我们需要在某个时候 * 丰富 *
getUserByEmail
提供的用户模型:显然,我们不需要这些额外的数据来知道用户是否存在,所以我们排除了基本的用户对象检索:
如果我们使用的是test 1,我们 * 根本不需要修改它 * -我们仍然在repo上使用
findByEmail
,内部实现已经改变的事实对我们的测试是不透明的。但是在测试2中,即使代码仍然做同样的事情,这也失败了。这是一个假阳性;功能工作,但测试失败。事实上,您可以在新特性明确需求之前应用该重构,提取
_getUser
;createUser
使用getUserByEmail
的事实直接反映了this.userRepository.findByEmail(email)
的 * 意外 * 复制-它们有不同的原因进行更改。或者想象一下,我们做了一些改变,* 打破了 *
getUserByEmail
。让我们模拟一个关于富集的问题,例如:如果我们使用测试1,我们对
createUser
的测试也会失败,但这是正确的结果!实现已中断,无法创建用户。对于测试2,我们有假阴性;测试通过,但功能不起作用。在这种情况下,你可以说最好看到 only
getUserByEmail
失败,因为这就是问题所在,但我认为当你查看代码时,这会非常令人困惑:* ”createUser
也调用了该方法,但测试表明它很好..."*。ars1skjm2#
您不应该模仿这些函数中的任何一个,因为它创建用户并从数据库阅读数据。如果你嘲笑他们,那测试还有什么意义。换句话说,你不知道你的应用程序是否正确地使用数据库。无论如何,我会模仿一些函数,比如发送电子邮件的函数等等。不要嘲笑作为应用程序核心的函数。您应该有一个用于测试的数据库和另一个用于生产的数据库。