Mock 函数是 Jest 中一种重要的测试工具,可以帮助我们创建虚拟的函数或者模拟现有函数的行为,以便于我们在测试代码的同时避免对真实环境的影响。在前端领域,Mock 函数适用于所有与服务器端交互的场景,包括网路请求、数据库操作、文件读写等等。
Jest 中的 Mock 函数
Jest 中提供了很多种 Mock 函数的实现方式,其中最常用的两种分别是手动 Mock 和自动 Mock。手动 Mock 的方式需要我们自己编写一个 Mock 函数,用于与真实函数进行替换,而自动 Mock 则是 Jest 根据一些特定的规则自动生成一个 Mock 函数。
手动 Mock
手动 Mock 可以通过编写一个与真实函数同名的 Mock 函数实现。例如下面这个函数用于获取用户信息:
async function getUserInfo(id) { return await fetch(`http://example.com/api/user/${id}`).then(res => res.json()); }
我们可以编写以下 Mock 函数替换 fetch
函数,以期待测试 getUserInfo 函数的使用情况。
global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({ name: 'wdv5011', age: 25 }) }));
通过 Mock 函数,我们使得 getUserInfo 函数可以获得一个固定的用户信息,而不是从真实接口中获取。在测试 getUserInfo
函数时,我们可以通过以下的方式检查函数的正确性:
test('getUserInfo should return the correct user info', async () => { const userInfo = await getUserInfo(1); expect(userInfo).toMatchObject({ name: 'wdv5011', age: 25 }); });
自动 Mock
自动 Mock 则是 Jest 提供的一种强大的 Mock 实现,可以简化 Mock 函数的编写过程。Jest 的自动 Mock 主要根据模块的导出来判断如何进行 Mock,包括模块函数、模块对象、模块类等等。
考虑以下模块文件:
// javascriptcn.com 代码示例 // user.js export default class User { constructor(name, age) { this.name = name; this.age = age; } async getInfo() { return await fetch(`http://example.com/api/user/${this.name}`).then(res => res.json()); } }
我们可以编写一个自动 Mock 如下:
// javascriptcn.com 代码示例 // __mock__/user.js export default class User { constructor(name, age) { this.name = name; this.age = age; } async getInfo() { return await Promise.resolve({ name: this.name, age: this.age }); } }
这样,在我们引入 user 模块时,Jest 会自动替换为自定义的 Mock 类。
Mock 返回值
在 Jest 中,可以通过 mockReturnValue
或者 mockResolvedValue
方法设置 Mock 函数的返回值。通过这些方法可以将 Mock 函数的行为更加细化,以便于测试更复杂的场景。
// javascriptcn.com 代码示例 test('getUserName should return the correct name', async () => { const getUserInfoMock = jest.fn(); getUserInfoMock.mockResolvedValue({ name: 'wdv5011', age: 25 }); const getUserName = async () => { const userInfo = await getUserInfoMock(1); return userInfo.name; } expect(await getUserName()).toBe('wdv5011'); expect(getUserInfoMock).toHaveBeenCalledWith(1); });
在这个例子中,我们通过 mockResolvedValue
方法指定了 getUserInfoMock 的返回值。同时,在函数调用之后,我们使用 Jest 提供的工具函数 toHaveBeenCalledWith
来检测 getUserInfoMock 是否被调用。
Mock 模块和导入
在 Jest 中,我们可以通过 jest.mock
来 Mock 整个模块。可以省略手动编写 Mock 函数的过程,直接使用一个虚拟的模块。Mock 一个模块时,我们可以通过 jest.doMock
来替换模块的导入。例如以下代码:
// javascriptcn.com 代码示例 // userService.test.js jest.mock('./user.js', () => { class MockUser { constructor(name, age) { this.name = name; this.age = age; } async getInfo() { return await Promise.resolve({ name: this.name, age: this.age }); } } return { default: MockUser }; }); test('userService should return the correct user info', async () => { const getUserService = require('./userService.js').default; const userService = new getUserService('wdv5011'); const userInfo = await userService.getInfo(); expect(userInfo).toMatchObject({ name: 'wdv5011', age: 25 }); });
在这个例子中,我们使用 jest.mock
来提供了一个替代的 Mock User 类。我们通过返回一个包含 MockUser 的对象,替换了对真实模块的引用。在测试代码中,我们可以通过 require
方法来获取到 Mock 模块,并用它创建出我们需要测试的 userService。最终,我们使用 Jest 的匹配器来检查函数的正确性。
更好的 Mock 函数管理
Mock 函数的使用让我们在前端开发的测试环节中,不再受到外部环境的影响。但是,使用多个 Mock 函数进行测试的时候,容易出现大量分散的 Mock 代码,影响了代码的可读性和维护性。因此,如何更好地管理 Mock 函数成为了一个重要的问题。
较佳实践
- 把 Mock 函数封装到单独的模块中,减少测试代码的冗余,同时在不同的测试文件中统一使用和管理 Mock 函数。
- Mock 函数应该明确其对应的实际模块或者函数,以便我们更好的理解和跟踪其行为。
- Mock 函数需要合理的分配测试范围,避免过于宽泛的 Mock 影响正常测试环节的准确性。
Vue 中 Mock 实践
对前后端数据交互的 Mock 是我们前端工程师一个必不可少的技能。例如在 Vue.js 中,我们可以使用 axios 来进行数据请求,而 axios 可以方便的被 Mock,以实现我们的测试需求。以下是一个在 Vue.js 中使用 Mock 函数的实践例子。
// javascriptcn.com 代码示例 // userService.js import axios from 'axios'; const userService = { async getUserInfo(id) { return await axios.post('/user/info', { id }); } }; export default userService;
在测试代码过程中,我们可以使用 jest.mock
来 Mock 掉 axios 模块,提供自己的 Mock Router:
// javascriptcn.com 代码示例 // userService.test.js import MockAdapter from 'axios-mock-adapter'; import userService from './userService'; import axios from 'axios'; const mock = new MockAdapter(axios); mock.onPost('/user/info').reply(200, { name: 'wdv5011' }); test('getUserInfo should return the correct user info', async () => { expect(await userService.getUserInfo(1)).toMatchObject({ name: 'wdv5011' }); });
在这里,我们使用了 axios-mock-adapter
作为 axios 的 Mock 函数库。通过在测试文件中初始化 MockAdapter 并使用它来 Mock 掉 axios,在测试完整的 getUserInfo
函数的过程中,我们不需要依赖真实的服务端,也可以获得我们期望的响应结果。
总结
Mock 函数是 Jest 中非常有用的一个技术,它能够帮助我们轻松实现虚拟的函数以及模块行为,以供我们在测试中使用。手动 Mock 和自动 Mock 是 Mock 实现中最常用的两种方式。在 Mock 函数的管理方面,我们需要合理的分类和作用范围,避免对测试结果造成误导。在实际测试场景中,Mock 函数经常被用在我们前后端数据交互的场景中,可以提供精简、可靠的测试代码,加速开发过程,提升代码质量。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/652fb6067d4982a6eb0e552b