You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Angular+Jest中用Office-Addin-Mock模拟Excel的NamedItemCollection

问题:模拟Excel命名范围数组时Mock对象无法处理数组的问题

我正尝试在Angular环境中用Jest模拟Excel的命名范围数组,测试自定义范围的增删操作。按照文档模拟Host对象并赋值给global.Excel后,遇到了以下问题:

  • 调用workbook.names.load()时,报错Cannot convert undefined or null to object
  • 改用context.load(workbook.names, 'items')时,报错Property _properties needs to be present in object model before load is called
  • 尝试workbook.names.load('items')时无报错,但workbook.names.items始终为undefined

注:这三种调用方式在正常加载项环境中均可正常运行。

测试代码

it('should delete the first item', async () => {

  const workbookWithNamedRanges = {
    context: {
      workbook: {
        names: {
          items: [
            {
              name: 'Test 1',
              type: 'Error'
            },
            {
              name: 'Test 2',
              type: 'Error'
            },
          ]
        }
      }
    },
    run: async function (callback: any) {
      await callback(this.context);
    }
  };


  const excelMock = new OfficeMockObject(workbookWithNamedRanges) as any;

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore
  global.Excel = excelMock;

  await service.service.removeFirstItem();

  expect(excelMock.context.workbook.names.items.length).toBe(1);
});
});

服务代码

async removeFirstItem(): Promise<void> {

  
  await Excel.run<void>(async context => {
    workbook = context.workbook;

    const namedRanges: Excel.NamedItemCollection = workbook.names.load() // <- Error here at .load;
    await context.sync();

    namedRanges.items.at(0)?.delete();

    await context.sync();


  });
}

错误信息

  1. 调用workbook.names.load()时:
Cannot convert undefined or null to object
TypeError: Cannot convert undefined or null to object
    at Function.keys (<anonymous>)
  1. 改用context.load(workbook.names, 'items')时:
Error: Property _properties needs to be present in object model before load is called.

    at C:\Users\User\Addins\addins\node_modules\office-addin-mock\src\officeMockObject.ts:168:15
    at Array.forEach (<anonymous>)
    at OfficeMockObject.parseObjectPropertyIntoArray (C:\Users\User\Addins\addins\node_modules\office-addin-mock\src\officeMockObject.ts:148:29)
    at OfficeMockObject.load (C:\Users\User\Addins\addins\node_modules\office-addin-mock\src\officeMockObject.ts:41:25)

解决建议

1. 修正Mock对象结构,适配OfficeMockObject要求

OfficeMockObject需要模拟对象包含_properties属性来处理load方法,集合类(如NamedItemCollection)也需要正确的Mock实现。调整Mock数据结构:

const workbookWithNamedRanges = {
  context: {
    workbook: {
      names: {
        items: [
          { name: 'Test 1', type: 'Error', delete: jest.fn() },
          { name: 'Test 2', type: 'Error', delete: jest.fn() },
        ],
        // Mock load方法,支持链式调用
        load: jest.fn((properties?: string) => this),
        _properties: ['items'] // 添加属性满足Mock库的加载校验
      }
    },
    // Mock sync方法,模拟实际同步行为
    sync: jest.fn().mockResolvedValue(undefined)
  },
  run: async function (callback: any) {
    await callback(this.context);
  }
};

2. 调整服务中的load调用方式

改为明确指定加载items属性,匹配Mock的逻辑:

const namedRanges: Excel.NamedItemCollection = workbook.names.load('items');

3. 手动Mock核心方法逻辑

如果OfficeMockObject对集合类支持有限,可完全手动Mock关键方法:

// 单独Mock命名范围集合
const namesMock = {
  items: [
    { name: 'Test 1', type: 'Error', delete: jest.fn() },
    { name: 'Test 2', type: 'Error', delete: jest.fn() },
  ],
  load: jest.fn(() => namesMock),
  _properties: []
};

// Mock上下文对象
const contextMock = {
  workbook: { names: namesMock },
  sync: jest.fn().mockResolvedValue(undefined)
};

// 最终的Excel Mock对象
const workbookWithNamedRanges = {
  context: contextMock,
  run: async (callback: (ctx: any) => Promise<void>) => {
    await callback(contextMock);
  }
};

4. 完善测试断言

除了校验数组长度,还可验证删除方法的调用情况:

expect(namesMock.items[0].delete).toHaveBeenCalled();

内容的提问来源于stack exchange,提问作者Harry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 19:05:25