如何在单元测试中从 ngrx 信号存储强制调用`onDestroy`钩子?

问题描述 投票:0回答:1

我在 Angular 项目中使用

SignalStore
中的
@ngrx/signals

我的商店有

onInit
onDestroy
钩子,从商店中注入的依赖项调用方法。

import { signalStore, withHooks } from '@ngrx/signals';

class SomeDependency {
    start() {
        throw new Error('real implementation not relevant');
    }
    stop() {
        throw new Error('real implementation not relevant');
    }
}

const MyStore = signalStore(
    withHooks((_store, dependency = inject(SomeDependency)) => ({
        onInit() {
            dependency.start();
        },
        onDestroy() {
            dependency.stop();
        },
    })),
);

我可以测试创建商店时是否正确调用

dependency.start()

it('should start dependency when created', () => {
    const fakeDependency = { start: jest.fn(), stop: jest.fn() };
    TestBed.configureTestingModule({
        providers: [MyStore, { provide: SomeDependency, useValue: fakeDependency }],
    });

    TestBed.inject(MyStore);

    expect(fakeDependency.start).toHaveBeenCalledOnce(); // test pass ✅
});

我正在寻找一种方法来测试当商店以某种方式被破坏时是否会调用

dependency.stop()
,但是如何强制商店被破坏?

it('should stop dependency when destroyed', () => {
    const fakeDependency = { start: jest.fn(), stop: jest.fn() };
    TestBed.configureTestingModule({
        providers: [MyStore, { provide: SomeDependency, useValue: fakeDependency }],
    });
    const store = TestBed.inject(MyStore);

    // MISSING: force store descruction here

    expect(fakeDependency.stop).toHaveBeenCalledOnce(); // test fail ❌
});
angular unit-testing jestjs ngrx-signal-store
1个回答
0
投票

除非持有该商店的主机被摧毁,否则该商店无法被摧毁。

因此,为了测试这个场景,我们创建一个名为

TestingComponent
的组件,它唯一的职责是在提供者数组中包含
MyStore
,通过这样做,我们可以使用
fixture.destroy()
销毁该组件,因为商店是一部分该组件的providers数组中,存储
onDestroy
钩子被调用,我们可以测试它。

import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';

import { signalStore, withHooks } from '@ngrx/signals';

export class SomeDependency {
  start() {
    throw new Error('real implementation not relevant');
  }
  stop() {
    throw new Error('real implementation not relevant');
  }
}

export const MyStore = signalStore(
  withHooks((_store, dependency = inject(SomeDependency)) => ({
    onInit() {
      dependency.start();
    },
    onDestroy() {
      dependency.stop();
    },
  }))
);

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h1>Hello from {{ name }}!</h1>
    <a target="_blank" href="https://angular.dev/overview">
      Learn more about Angular
    </a>
  `,
})
export class App {
  name = 'Angular';
}

bootstrapApplication(App);

Stackblitz 演示

© www.soinside.com 2019 - 2024. All rights reserved.