如何使用Sinon模拟Azure Blob Storage中ContainerClient的listBlobsByHierarchy方法返回值?
如何用Sinon模拟Azure Blob Storage的ContainerClient.listBlobsByHierarchy方法
我来帮你搞定这个问题——模拟listBlobsByHierarchy确实有点特殊,因为它返回的是异步分页迭代器(PagedAsyncIterableIterator),不是普通数组。下面是一步步的具体实现方案,完全适配你的测试需求:
核心思路
要让Sinon正确Stub这个方法,关键是构造一个符合PagedAsyncIterableIterator接口的模拟对象,它需要支持for await...of遍历(这是你代码里用到的逻辑),同时满足Azure SDK的类型要求。
步骤1:构造模拟的Blob数据
先创建你需要的BlobItem和BlobPrefix实例,和真实返回的结构对齐:
// 模拟你的业务中需要的BlobItem const mockBlobItems = [ { kind: "blob", name: "docs/report.pdf", properties: { contentType: "application/pdf", lastModified: new Date("2024-01-01") }, // 其他你代码中用到的属性都可以加上 }, { kind: "blob", name: "images/avatar.png", properties: { contentType: "image/png", lastModified: new Date("2024-02-15") }, } ]; // 如果你的代码需要处理前缀(比如文件夹),可以模拟BlobPrefix const mockBlobPrefixes = [ { kind: "prefix", name: "archive/" } ];
步骤2:实现模拟的异步分页迭代器
我们需要构建一个对象,满足PagedAsyncIterableIterator的核心要求:支持异步迭代,能被for await...of遍历。这里写一个通用的构造函数:
function createMockPagedIterator(items) { let currentIndex = 0; return { // 实现迭代器的next方法,返回Promise async next() { if (currentIndex < items.length) { return { value: items[currentIndex++], done: false }; } return { done: true }; }, // 可选:如果你的代码用到了分页(调用byPage()),可以补充这个方法 byPage: async function* () { // 这里模拟单页返回所有数据,也可以根据需求拆分成分页 yield items; }, // 必须实现Symbol.asyncIterator,才能支持for await...of [Symbol.asyncIterator]() { return this; } }; } // 合并BlobItem和BlobPrefix,生成完整的迭代器数据 const mockIteratorData = [...mockBlobItems, ...mockBlobPrefixes]; const mockPagedIterator = createMockPagedIterator(mockIteratorData);
步骤3:用Sinon Stub替换目标方法
根据你的代码结构,分两种常见情况处理:
情况1:ContainerClient通过依赖注入传入
如果你的API模块是通过参数接收ContainerClient实例(推荐的最佳实践),直接Stub实例的方法即可:
const sinon = require('sinon'); const { expect } = require('chai'); // 或你使用的断言库 const yourApiModule = require('../path-to-your-api-module'); describe('你的REST接口测试', () => { let containerClientStub; beforeEach(() => { // 创建ContainerClient的Stub对象,替换listBlobsByHierarchy方法 containerClientStub = { listBlobsByHierarchy: sinon.stub().returns(mockPagedIterator) }; // 注入Stub到你的API模块中 yourApiModule.init(containerClientStub); }); it('应该正确遍历Blob并转换格式', async () => { // 调用你的接口处理逻辑 const transformedResult = await yourApiModule.processBlobs(); // 验证方法被正确调用(比如传入了预期的分隔符和选项) expect(containerClientStub.listBlobsByHierarchy.calledWith('/', { /* 你的预期选项 */ })).to.be.true; // 验证转换后的结果符合预期 expect(transformedResult).to.deep.equal([ { fileName: 'report.pdf', type: 'application/pdf' }, { fileName: 'avatar.png', type: 'image/png' } ]); }); });
情况2:直接导入@azure/storage-blob模块
如果你的代码是直接导入ContainerClient类,可以Stub它的原型方法:
const sinon = require('sinon'); const { ContainerClient } = require('@azure/storage-blob'); const yourApiModule = require('../path-to-your-api-module'); describe('你的REST接口测试', () => { let listBlobsStub; beforeEach(() => { // Stub ContainerClient原型上的listBlobsByHierarchy方法 listBlobsStub = sinon.stub(ContainerClient.prototype, 'listBlobsByHierarchy').returns(mockPagedIterator); }); afterEach(() => { // 测试结束后恢复原型,避免影响其他测试用例 listBlobsStub.restore(); }); it('应该正确处理Blob数据', async () => { const result = await yourApiModule.processBlobs(); // 验证方法被调用 expect(listBlobsStub.called).to.be.true; // 验证结果正确性 expect(result).to.deep.equal(/* 你的预期结果 */); }); });
额外注意事项
- 如果你的代码用到了分页参数(比如
maxPageSize),可以修改createMockPagedIterator的byPage方法,模拟多页返回的逻辑。 - 确保模拟的
BlobItem/BlobPrefix包含你代码中用到的所有属性,避免因为属性缺失导致测试失败。 - 用Sinon的
calledWith、callCount等断言方法,验证方法的调用参数和次数,确保你的业务逻辑符合预期。
内容的提问来源于stack exchange,提问作者gsakthivel
相关产品推荐
相关产品推荐

