如何用vi.mock来模拟我们自定义的ResizeObserver指令中使用的ResizeObserver

前端开发 2026-07-11

在我们的nx/Angular 21环境中,我们开发了一个自定义的ResizeObserverDirective(底层使用的是标准的ResizeObserver)。

在测试中,我们在 test-set.ts 文件里尝试使用 vi.fn() 对它进行模拟时遇到了问题:

vi.mock('@abc/my-common-ui', async importOriginal => {
  const originalModule = await importOriginal();

  const { of } = await import('rxjs');
  const ResizeObserver = vi.fn(
    class MockClass {
      observe = vi.fn();
      unobserve = vi.fn();
      disconnect = vi.fn();
    }
  );

  const SvgCacheService = vi.fn();

  return {
    ...(originalModule || {}),
    ResizeObserver,
    SvgCacheService,  
  };
});

在对其中一个文件运行测试后,我们得到如下错误:

 Error: [vitest] There was an error when mocking a module. If you are using "vi.mock" factory, make sure there are no top level variables inside, since this call is hoisted to top of the file. Read more: https://vitest.dev/api/vi.html#vi-mock

Caused by: ReferenceError: ResizeObserver is not defined
 ❯ ../../node_modules/@abc/my-common-ui/fesm2022/abc-common-ui.mjs:1102:24
 ❯ src/test-setup.ts:24:26

错误中的第1102行来自abc-common-ui.mjs --> const resizeObserver = new ResizeObserver((entries) => { ... }

我是不是把模拟对象弄错了?

更新: 我们目前的权宜之计是在 test-setup.ts 中添加 globalThis(并在上方移除 vi.mock('@abc/my-common-ui')):

globalThis.ResizeObserver = class {
  observe() {}
  unobserve() {}
  disconnect() {}
} as any;

然而这种方法还需要在每个 vi.mock() 内使用动态导入,例如 await import('<module>'),与在文件顶部进行静态导入相比。

使用这些动态导入也会在组件的spec.ts文件中引发ESLint警告(Static imports of lazy-loaded libraries are forbidden)。

解决方案

到目前为止,我们认为最佳/最干净的解决办法是如下所示:

  1. 通过 vi.stubGlobal() 在test-setup.ts中对ResizeObserverMock进行存根

  2. 参见 https://vitest.dev/guide/mocking/globals.html

  3. 将单独的mocks移到组件的单元测试里本身 — 即 vi.mock()

  4. 这也解决了上文提到的eslint警告“static import of lazy-loaded...”。

  5. test-setup.ts

``` import '@analogjs/vitest-angular/setup-snapshots'; import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';

setupTestBed();

const ResizeObserverMock = class { observe(): void { // void } unobserve(): void { // void } disconnect(): void { // void } };

vi.stubGlobal('ResizeObserver', ResizeObserverMock); ``` * my-vew.component.spec.ts

describe('OurViewComponent', () => {
  let component: MyViewComponent;
  let fixture: ComponentFixture<MyViewComponent>;

  vi.mock('@myLib/my-data', async importOriginal => {
    const actual = await importOriginal();
    const MyDataLoadService = vi.fn(
      class MockClass {
        loadODetail = vi.fn(() => of());
        loadODataset = vi.fn();
      }
    );

    const MyDataFullLoadServiceMock = vi.fn(
      class MockClass {
        loadODetail = vi.fn(() => of());
        loadODataset = vi.fn();
      }
    );

    return {
      ...(actual || {}),
      MyDataLoadService ,
      MyDataFullLoadServiceMock ,
    };
  });

  beforeEach(async() => {
    /// test setup code here...
  });
  it('should create', async () => {
    expect(component).toBeTruthy();
  });
});
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章