使用Jest编写JS异步函数单元测试:Mock及authClient报错解决
解决Jest测试异步函数时
authClient is not defined的问题及完整测试方案 先修复代码中的基础问题
你的代码存在几个语法/导出问题,这是导致报错的直接原因:
myClientManager.js:
- 模块名拼写错误:
@microsoft/mirosoft-graph-client→@microsoft/microsoft-graph-client - 未导出
authClient函数,需添加导出语句 - 缺少闭合括号,补全函数结构
修复后代码:
const graph = require('@microsoft/microsoft-graph-client'); require('isomorphic-fetch'); const authClient = (accessToken) => { return graph.Client.init({ authProvider: (done) => { done(null, accessToken); } }); }; module.exports = { authClient };- 模块名拼写错误:
myClient.js:
req.header应为req.headers(拼写错误)- 括号不匹配:
authClient(req.header['px-token'];缺少闭合括号 - 假设
conFigItem是项目配置变量,测试时会Mock它
修复后代码:
const { authClient } = require("../service/gMemry"); // 确保路径正确,gMemry应为导出authClient的文件名(比如myClientManager.js改名为gMemry.js) const getResource = async (req, res) => { try { const gMemory = authClient(req.headers['px-token']); const result = await gMemory.api(conFigItem.auth.clientId).get(); if (!result) { return res.sendStatus(401).send(); } res.send({ result }); } catch (e) { res.sendStatus(401).send({ result: null }); } }; module.exports = { getResource };
编写Jest单元测试的步骤
1. Mock依赖模块
在测试文件中Mock../service/gMemry模块的authClient函数,以及它返回的Graph客户端对象的方法,避免调用真实第三方服务。
2. 模拟请求和响应对象
创建Mock的req和res对象,其中res的sendStatus、send方法用jest.fn()模拟,方便后续断言调用情况。
3. 编写测试用例
覆盖成功返回结果、无结果返回401、捕获异常返回401三种核心场景。
完整的myClient.spec.js代码:
const { getResource } = require("./myClient"); // 导入正确路径 const { authClient } = require("../service/gMemry"); // Mock整个gMemry模块 jest.mock("../service/gMemry", () => ({ authClient: jest.fn() })); describe('getResource', () => { // 每次测试前重置Mock函数,避免测试间副作用 beforeEach(() => { jest.clearAllMocks(); }); it('成功获取资源时返回结果', async () => { // 模拟authClient返回的客户端对象链式调用 const mockApi = jest.fn().mockReturnThis(); const mockGet = jest.fn().mockResolvedValue({ data: 'test-resource' }); authClient.mockReturnValue({ api: mockApi, get: mockGet }); // 模拟请求、响应对象 const mockReq = { headers: { 'px-token': 'valid-token' } }; const mockRes = { sendStatus: jest.fn().mockReturnThis(), send: jest.fn() }; // Mock全局配置变量 global.conFigItem = { auth: { clientId: 'test-client-id' } }; await getResource(mockReq, mockRes); // 断言各方法调用正确性 expect(authClient).toHaveBeenCalledWith('valid-token'); expect(mockApi).toHaveBeenCalledWith('test-client-id'); expect(mockGet).toHaveBeenCalled(); expect(mockRes.send).toHaveBeenCalledWith({ result: { data: 'test-resource' } }); expect(mockRes.sendStatus).not.toHaveBeenCalled(); }); it('获取结果为空时返回401', async () => { const mockApi = jest.fn().mockReturnThis(); const mockGet = jest.fn().mockResolvedValue(null); authClient.mockReturnValue({ api: mockApi, get: mockGet }); const mockReq = { headers: { 'px-token': 'valid-token' } }; const mockRes = { sendStatus: jest.fn().mockReturnThis(), send: jest.fn() }; global.conFigItem = { auth: { clientId: 'test-client-id' } }; await getResource(mockReq, mockRes); expect(mockRes.sendStatus).toHaveBeenCalledWith(401); expect(mockRes.send).toHaveBeenCalled(); }); it('捕获异常时返回401', async () => { const mockApi = jest.fn().mockReturnThis(); const mockGet = jest.fn().mockRejectedValue(new Error('Auth failed')); authClient.mockReturnValue({ api: mockApi, get: mockGet }); const mockReq = { headers: { 'px-token': 'invalid-token' } }; const mockRes = { sendStatus: jest.fn().mockReturnThis(), send: jest.fn() }; global.conFigItem = { auth: { clientId: 'test-client-id' } }; await getResource(mockReq, mockRes); expect(mockRes.sendStatus).toHaveBeenCalledWith(401); expect(mockRes.send).toHaveBeenCalledWith({ result: null }); }); });
关键知识点说明
- Jest.mock():Mock整个依赖模块,隔离真实第三方服务调用
- jest.fn():创建Mock函数,用于模拟方法行为并断言调用次数、参数、返回值
- async/await:处理异步函数测试,确保测试等待异步操作完成后再执行断言
- beforeEach:在每个测试用例前重置Mock状态,避免测试间的干扰
内容的提问来源于stack exchange,提问作者Jeet
相关产品推荐
相关产品推荐

