如何模拟@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
并将其添加到 TestBed
的 providers
:
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
对象上调用它 - 如其他答案中所述。
我想对组件进行单元测试。我正在使用 @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
并将其添加到 TestBed
的 providers
:
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
对象上调用它 - 如其他答案中所述。