如何用vi.mock来模拟我们自定义的ResizeObserver指令中使用的ResizeObserver
在我们的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)。
解决方案
到目前为止,我们认为最佳/最干净的解决办法是如下所示:
-
通过
vi.stubGlobal()在test-setup.ts中对ResizeObserverMock进行存根 -
将单独的mocks移到组件的单元测试里本身 — 即
vi.mock() -
这也解决了上文提到的eslint警告“static import of lazy-loaded...”。
-
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();
});
});