如何模拟@ngrx/store?

How to mock @ngrx/store?

我想对组件进行单元测试。我正在使用 @ngrx/store 和 rxjs 来从我的组件更新我的商店。为此,我订阅和取消订阅商店。 我的单元测试失败

TypeError: undefined is not an object (evaluating 'this.store.unsubscribe') in config/spec-bundle.js

我无法找到如何正确模拟我的商店及其事件发射器。

以下几行:

app.component.ts :

  ngOnDestroy() {
    // unsubscribe the observable
    this.userStore.unsubscribe();
  }

app.component.spec.ts :

我是如何模拟商店的:

        export class MockStore extends Subject<fromRoot.State> implements Store <fromRoot.State> {}

我是如何配置的:

        TestBed.configureTestingModule({
        imports: [
            FormsModule,
            ReactiveFormsModule,
            StoreModule.provideStore(reducer)
        ],
        declarations: [LoginComponent],
        providers: [
            BaseRequestOptions,
            MockBackend,
            { provide: AuthService, useValue: authServiceStub },
            { provide: LoginService, useValue: loginServiceStub },
            { provide: LoggerService, useValue: loggerServiceStub },
            {
                provide: Http, useFactory: (backend: ConnectionBackend,
                    defaultOptions: BaseRequestOptions) => {
                    return new Http(backend, defaultOptions);
                }, deps: [MockBackend, BaseRequestOptions]
            },
            { provide: Router, useClass: MockRouter }
        ]
  })

如果您需要更多信息,请告诉我。提前致谢。

问题可能与您的退订有关。您应该在实际订阅时调用取消订阅而不是 observable/store。 例如。如果您有这样的订阅:

this.subscription = this._store
    .select('users')
    .subscribe(users => {
      this.users = users;
  });

您这样取消订阅:

  ngOnDestroy() {
    // unsubscribe the observable
    this.subscription.unsubscribe();
  }

此外,不确定您是否遗漏了一些代码,但您模拟商店的方式可能只使用实际商店就足够了,只需监视调度等。

模拟商店的方法不止一种,具体操作取决于您要如何测试。

当我在测试中模拟商店时,我只对两件事感兴趣:

  • 能够轻松发出与测试相关的状态;和
  • 能够看到在 test.the
  • 期间发送到商店的任何操作

我对连接减速器不感兴趣,因为它们在别处进行过测试,所以这就是我所做的。

我使用这样的函数从两个 Subject 实例创建一个 Store

import { Action, Store } from "@ngrx/store";
import { Subject } from "rxjs/Subject";

export function mockStore<T>({
  actions = new Subject<Action>(),
  states = new Subject<T>()
}: {
  actions?: Subject<Action>,
  states?: Subject<T>
}): Store<T> {

  let result = states as any;
  result.dispatch = (action: Action) => actions.next(action);
  return result;
}

然后在 beforeEach 中创建 Store 并将其添加到 TestBedproviders:

actions = new Subject<Action>();
states = new Subject<AppState>();
store = mockStore<AppState>({ actions, states });

TestBed.configureTestingModule({
  imports: [EffectsTestingModule],
  providers: [
    {
      provide: Store,
      useValue: store
    },
    ...
  ]
});

通过将模拟的 Store 注入组件 - 或效果,或任何您正在测试的 - 然后在测试中发出特定状态很简单 - 使用 states 主题 - 它是通过订阅 actions 主题,很容易检查是否已发送任何预期的操作。

关于您的错误,您不应该在 Store 上调用 unsubscribe;您应该在订阅商店时返回的 Subscription 对象上调用它 - 如其他答案中所述。