Jest入门:如何测试依赖无法实例化Session类参数的异步函数?
你好!这种场景在单元测试里太常见了,完全可以通过 Mock Session 类的实例来解决,不用纠结无法创建真实实例的问题。下面我一步步给你讲怎么实现:
核心思路:Mock 依赖而非使用真实实例
我们不需要真实的 Session 实例,只需要构建一个符合函数调用需求的假对象——也就是 Mock 出 initFlow 里用到的 Session 属性和方法即可,比如 userInput.getParam、features.useTerms、botId 这些。同时,还要把函数里依赖的其他模块(AtomManager、UserManager 等)也 Mock 掉,确保测试只聚焦于 initFlow 本身的逻辑。
具体实现步骤
1. 构建 Mock Session 对象
根据 initFlow 里用到的 Session 成员,创建一个包含对应属性和 Mock 方法的对象:
// 如果你用 TypeScript,最后可以用 as unknown as Session 做类型断言 const mockSession = { userInput: { getParam: jest.fn() // Mock getParam 方法 }, features: { useTerms: false // 可根据测试场景切换 true/false }, botId: 'test-bot-id', user: null // 后续测试中可动态修改 };
2. Mock 依赖的外部模块
initFlow 里调用了 AtomManager、UserManager 等模块的方法,单元测试需要隔离这些外部依赖,用 Jest 直接 Mock 整个模块:
// Mock AtomManager jest.mock('./path/to/AtomManager', () => ({ findActiveAtom: jest.fn(), getStartAtom: jest.fn() })); // Mock UserManager jest.mock('./path/to/UserManager', () => ({ getGlobalUser: jest.fn() })); // Mock AtomProcessor jest.mock('./path/to/AtomProcessor', () => ({ processAtom: jest.fn() }));
3. 编写不同分支的测试用例
针对 initFlow 的各个逻辑分支,编写对应的测试:
describe('initFlow', () => { // 每次测试前重置所有 Mock 的调用记录和返回值 beforeEach(() => { jest.clearAllMocks(); }); // 测试分支1:存在 nextAtomId 且满足条件时,返回 processAtom 的结果 it('should return processAtom result when valid nextAtomId exists', async () => { // 1. 设置 Mock 返回值 const mockNextAtomId = 'test-atom-123'; const mockNextAtom = { id: mockNextAtomId, type: 'beforeTerms' }; const mockProcessResult = { status: 'success' }; mockSession.userInput.getParam.mockReturnValue(mockNextAtomId); AtomManager.findActiveAtom.mockResolvedValue(mockNextAtom); AtomProcessor.processAtom.mockResolvedValue(mockProcessResult); // 2. 调用被测函数 const result = await initFlow(mockSession); // 3. 断言逻辑正确性 expect(AtomManager.findActiveAtom).toHaveBeenCalledWith(mockNextAtomId); expect(AtomProcessor.processAtom).toHaveBeenCalledWith(mockSession, mockNextAtom); expect(result).toEqual(mockProcessResult); }); // 测试分支2:不存在 nextAtomId 时,使用起始 atom 并初始化用户 it('should use start atom and initialize user when no nextAtomId', async () => { // 设置 Mock:getParam 返回 null,模拟无 nextAtomId 的情况 mockSession.userInput.getParam.mockReturnValue(null); const mockStartAtom = { id: 'start-atom', type: 'default' }; const mockUser = { id: 'test-user-456' }; const mockProcessResult = { status: 'started' }; AtomManager.getStartAtom.mockResolvedValue(mockStartAtom); UserManager.getGlobalUser.mockResolvedValue(mockUser); AtomProcessor.processAtom.mockResolvedValue(mockProcessResult); const result = await initFlow(mockSession); expect(AtomManager.getStartAtom).toHaveBeenCalledWith(mockSession.botId); expect(UserManager.getGlobalUser).toHaveBeenCalledWith(mockSession); expect(mockSession.user).toEqual(mockUser); expect(AtomProcessor.processAtom).toHaveBeenCalledWith(mockSession, mockStartAtom); expect(result).toEqual(mockProcessResult); }); // 测试分支3:找不到起始 atom 时抛出错误 it('should throw error when start atom is not found', async () => { mockSession.userInput.getParam.mockReturnValue(null); AtomManager.getStartAtom.mockResolvedValue(null); // 断言函数抛出指定错误 await expect(initFlow(mockSession)).rejects.toThrow('Could not find start atom'); expect(AtomManager.getStartAtom).toHaveBeenCalledWith(mockSession.botId); }); });
TypeScript 类型兼容小技巧
如果用 TypeScript,直接用 Mock 对象可能会报类型错误,这时候可以用两种方式解决:
- 用
as unknown as Session对 Mock 对象做类型断言 - 直接 Mock 整个
Session类,生成类型兼容的实例:
jest.mock('./path/to/Session', () => { return jest.fn().mockImplementation(() => ({ userInput: { getParam: jest.fn() }, features: { useTerms: false }, botId: 'test-bot-id', user: null })); }); // 创建类型兼容的 Mock 实例 const mockSession = new Session() as jest.Mocked<Session>;
内容的提问来源于stack exchange,提问作者dragalur
相关产品推荐
相关产品推荐

