一、引言
在进行单元测试时,我们经常会遇到对外部资源的依赖,如数据库、网络接口等。模拟(Mocking)和存根(Stubbing)是两种帮助我们模拟这些外部资源,使我们能够在隔离环境中测试单元的方法。在本文中,我们将详细介绍模拟和存根,以及如何在单元测试中使用它们。
二、模拟和存根是什么?
模拟和存根都是用来模拟外部资源的技术,但它们的关注点略有不同:
-
存根(Stubs):存根是用来提供预设行为的。它们在你调用方法时返回一个特定的值或抛出一个异常。
-
模拟(Mocks):模拟不仅提供预设行为,还能检查调用的次数、参数等。通常我们用模拟来验证一个对象是否正确地使用了另一个对象。
三、使用模拟和存根的例子
下面,我们将使用JavaScript中的流行测试库,Jest,来展示如何使用模拟和存根。
例1:使用存根
假设你有一个函数getUserName
,它从数据库中获取用户的名字。你想要测试这个函数,但是不想依赖真实的数据库。
-
async function getUserName(userId) {
-
const user = await db.getUser(userId);
-
return user.name;
-
}
你可以使用Jest的jest.fn()
方法来创建一个存根:
-
const db = {
-
getUser: jest.fn()
-
};
-
db.getUser.mockResolvedValue({ name: 'Alice' });
-
expect(await getUserName(1)).toBe('Alice');
在这个例子中,我们创建了一个存根来代替db.getUser
方法。这个存根总是返回一个具有名字"Alice"的用户。
例2:使用模拟
假设你有一个login
函数,它调用了一个authentication
服务来验证用户的凭据。你想要测试这个函数,但是你更关心它是否正确地调用了authentication
服务。
-
async function login(username, password) {
-
return await authentication.authenticate(username, password);
-
}
你可以使用Jest的jest.fn()
方法来创建一个模拟:
-
const authentication = {
-
authenticate: jest.fn()
-
};
-
authentication.authenticate.mockResolvedValue(true);
-
await login('Alice', 'password');
-
expect(authentication.authenticate).toHaveBeenCalledWith('Alice', 'password');
在这个例子中,我们创建了一个模拟来代替authentication.authenticate
方法。然后,我们使用toHaveBeenCalledWith
方法来验证login
函数是否正确地调用了authentication.authenticate
方法。
四、结论
模拟和存根是单元测试中的重要工具。它们帮助我们创建隔离的测试环境,使我们能够集中测试代码的特定部分,而不必担心其他外部依赖项。
下面是一些更深入使用模拟和存根的示例:
例3:更复杂的存根行为
在有些情况下,你可能想要模拟一些更复杂的行为,例如抛出异常。你可以通过配置存根来实现这一点。
-
const db = {
-
getUser: jest.fn()
-
};
-
db.getUser.mockRejectedValue(new Error('User not found'));
-
try {
-
await getUserName(1);
-
} catch (e) {
-
expect(e.message).toBe('User not found');
-
}
在这个例子中,我们配置存根在被调用时抛出一个错误。然后,我们在测试中捕获这个错误,并验证它的消息是否正确。
例4:模拟函数调用的次数
除了验证模拟函数是否被正确的参数调用,你还可以验证模拟函数被调用的次数。
-
const authentication = {
-
authenticate: jest.fn()
-
};
-
authentication.authenticate.mockResolvedValue(true);
-
await login('Alice', 'password');
-
await login('Bob', 'password');
-
expect(authentication.authenticate).toHaveBeenCalledTimes(2);
在这个例子中,我们使用toHaveBeenCalledTimes
方法来验证login
函数是否调用了两次authentication.authenticate
方法。
五、结语
模拟和存根是一种强大的工具,可以帮助你编写出更加健壮和可维护的单元测试。通过了解何时以及如何使用这些工具,你可以更有效地测试你的代码,而无需担心外部依赖的复杂性。希望以上的示例可以帮助你在实践中更好地理解和应用模拟和存根。
六、参考链接
-
Jest: https://jestjs.io/
-
Sinon (另一款支持存根和模拟的JavaScript测试库): http://sinonjs.org/
行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 320231853,里面有各种软件测试+开发资料和技术可以一起交流学习哦。
最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:
这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!