如何为使用Azure存储文件共享、服务总线的代码编写Jest单元测试
可行测试方案
一共两类实现路径,都不需要连接真实Azure服务,本地开发和GitHub CI流水线都能直接跑,适配Jest+NestJS的技术栈。
方案一:纯Jest Mock实现(适合单元测试,零额外依赖)
这是单元测试场景下最轻量的方案,不需要启动任何额外服务,测试执行速度最快。核心逻辑是通过Jest的mock能力,替换掉@azure/storage-file-share、@azure/service-bus SDK中实际发起网络请求的客户端实现,返回你预设的测试数据,只验证业务逻辑本身的正确性。
Azure File Share 测试实现
如果你在NestJS中是通过依赖注入的方式注入ShareClient、ShareFileClient实例,直接在测试模块中替换为mock实例即可:
// files.service.spec.ts import { Test, TestingModule } from '@nestjs/testing'; import { ShareFileClient } from '@azure/storage-file-share'; import { FilesService } from './files.service'; describe('FilesService', () => { let service: FilesService; let mockFileClient: jest.Mocked<ShareFileClient>; beforeEach(async () => { // 构造mock客户端,只需要实现你业务代码里实际调用的方法 mockFileClient = { downloadFileToBuffer: jest.fn(), uploadFile: jest.fn(), deleteFile: jest.fn(), } as unknown as jest.Mocked<ShareFileClient>; const module: TestingModule = await Test.createTestingModule({ providers: [ FilesService, { provide: 'SHARE_FILE_CLIENT', useValue: mockFileClient } ], }).compile(); service = module.get<FilesService>(FilesService); }); it('should correctly get file content from share', async () => { const testContent = Buffer.from('test file content'); // 模拟SDK返回结果 mockFileClient.downloadFileToBuffer.mockResolvedValue([testContent]); const res = await service.getFileContent('test/path.txt'); expect(res).toEqual(testContent); expect(mockFileClient.downloadFileToBuffer).toHaveBeenCalledTimes(1); }); });
如果不想逐个方法手动构造mock,可以直接用jest.mock()全局mock整个SDK包,所有客户端调用都会自动替换为mock函数:
jest.mock('@azure/storage-file-share', () => { const mockFileClient = { downloadFileToBuffer: jest.fn(), uploadFile: jest.fn() }; return { ShareClient: jest.fn().mockImplementation(() => ({ getFileClient: jest.fn().mockReturnValue(mockFileClient) })), ShareFileClient: jest.fn() } });
Azure Service Bus 测试实现
逻辑和File Share完全一致,mock你业务用到的ServiceBusClient、消息发送器、接收器的方法即可:
jest.mock('@azure/service-bus', () => { const mockSender = { sendMessages: jest.fn().mockResolvedValue(undefined), close: jest.fn().mockResolvedValue(undefined) }; const mockReceiver = { receiveMessages: jest.fn().mockResolvedValue([{ body: { orderId: 1, status: 'paid' } }]), close: jest.fn().mockResolvedValue(undefined) }; return { ServiceBusClient: jest.fn().mockImplementation(() => ({ createSender: jest.fn().mockReturnValue(mockSender), createReceiver: jest.fn().mockReturnValue(mockReceiver), close: jest.fn().mockResolvedValue(undefined) })) } });
写测试用例的时候,只需要断言对应方法有没有被正确调用、入参是否符合预期、业务逻辑有没有正确处理SDK返回的结果即可。
这个方案的缺点是需要自己维护mock的方法和返回结构,和真实服务的行为可能存在细微差异,适合纯单元测试场景。
方案二:本地模拟器实现(适合集成测试,行为贴近真实服务)
如果你需要覆盖SDK调用、参数校验、请求流程这类纯mock覆盖不到的逻辑,可以用兼容Azure SDK的本地模拟器,不需要连接真实云服务,本地和CI都能快速启动。
- Azure File Share 模拟器:用Azurite,是Azure官方维护的开源存储模拟器,完整支持File Share、Blob、Queue的所有接口,和官方SDK完全兼容。启动后直接用模拟器默认的连接串初始化SDK客户端即可,不需要修改业务代码。本地可以直接通过npm全局安装启动,CI环境可以直接用npm命令启动,也可以用容器启动。
启动命令参考:azurite --silent --sharePort 10000 - Azure Service Bus 模拟器:用社区维护的开源Service Bus内存模拟器,兼容官方SDK的核心接口,支持队列、主题、订阅的消息收发逻辑,启动后用默认连接串即可连接,CI环境用容器一行命令就能启动。
这个方案的优点是不需要写大量mock代码,SDK交互逻辑和真实服务几乎一致,测试可信度更高;缺点是测试启动速度比纯mock慢一点,但整体耗时完全在可接受范围内。
GitHub Actions流水线适配
两种方案都不需要给流水线配置Azure账号权限:
- 纯mock方案:直接在流水线中执行
npm run test即可,和普通单元测试没有区别。 - 模拟器方案:在执行测试的步骤前,加一步启动对应模拟器的步骤即可,GitHub托管运行器默认自带node和docker环境,不需要额外配置。注意测试环境下要把服务连接串改成模拟器的本地地址,建议通过环境变量注入,不要硬编码。
注意:如果用模拟器方案,不需要做特殊的网络配置,模拟器都是跑在本地环回地址,不会发起公网请求。
内容的提问来源于stack exchange,提问作者BoyWithLaziness
相关产品推荐
相关产品推荐

