如何测试使用依赖注入服务事件的 NgRx Effect
How to test NgRx Effect that uses event from dependency injected service
我有以下效果,它使用来自 Angulars 依赖注入提供的 PlatformService 的可观察源。
public resume$ = createEffect(() => {
return this.platformService.resume().pipe(mapTo(PlatformActions.appResumed()));
});
constructor(private platformService: PlatformService) {
}
这确实很好用,但我真的不知道如何优雅地测试它。通常我们会尝试将我们的 TestingModule 设置得尽可能不具体。 e.G
describe('PlatformEffects', () => {
let effects: PlatformEffects;
let platformServiceSpy: SpyObj<PlatformService>;
beforeEach(() => {
platformServiceSpy = jasmine.createSpyObj({
resume: EMPTY
});
TestBed.configureTestingModule({
providers: [
PlatformEffects,
{
provide: PlatformService,
useValue: platformServiceSpy
}
]
});
effects = TestBed.inject(PlatformEffects);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('resume$', () => {
it('dispatches appResumed action on resume event', (done) => {
// AAA: This test should setup the response from the platformService so the arrange setup is coupled in the test.
});
});
});
遗憾的是,这似乎不起作用,因为效果是在构造函数中创建的,因此我的事件流始终为 EMPTY,即使我覆盖了间谍中的恢复响应也不会触发效果。
现在我可以在 beforeEach 中设置一个默认值(例如 of(undefined)
),但是它在我的测试中并没有真正耦合,我不能使用 AAA 模式。
另一方面,我可能每次都创建一个新的 Effects 实例,但这似乎有点过分了不是吗?
有没有更好的方法? NgRx 用动作流很好地解决了这个问题,我想知道是否有类似的解决方案来处理使用来自 DI 的源的效果。
有关详细信息,请参阅 https://timdeschryver.dev/blog/testing-an-ngrx-project#effects
it('fetch$ dispatches a success action', () => {
// The Effect Actions stream is created by instantiating a new `ActionsSubject`
const actions = new ActionsSubject();
const effects = new CustomersEffects(actions, newCustomerService());
// Subscribe on the effect to catch emitted actions, which are used to assert the effect output
const result: Action[] = [];
effects.fetch$.subscribe((action) => {
result.push(action);
});
const action = customerPageActions.enter({ customerId: '3' });
actions.next(action);
expect(result).toEqual([
customersApiActions.fetchCustomerSuccess(
newCustomer({
id: action.customerId,
}),
),
]);
});
我尝试了更多,认为操作流可能只是一个主题,我可以为我自己的服务方法做同样的事情:
describe('PlatformEffects', () => {
let effects: PlatformEffects;
let platformServiceSpy: SpyObj<PlatformService>;
let resumeSubject: ReplaySubject<any>;
beforeEach(() => {
// create new subject that can be used as an event emitter
resumeSubject = new ReplaySubject<any>();
platformServiceSpy = jasmine.createSpyObj({
resume: resumeSubject.asObservable()
});
TestBed.configureTestingModule({
providers: [
PlatformEffects,
{
provide: PlatformService,
useValue: platformServiceSpy
}
]
});
effects = TestBed.inject(PlatformEffects);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('resume$', () => {
it('dispatches appResumed action on resume event', (done) => {
// emit new test string in resume subject
resumeSubject.next('test');
effects.resume$.subscribe((res) => {
expect(res).toEqual(PlatformActions.appResumed());
done();
});
});
});
});
这适用于这种情况,但一旦我尝试对 returns 承诺此解决方案不再有效的服务方法执行相同操作。
我有以下效果,它使用来自 Angulars 依赖注入提供的 PlatformService 的可观察源。
public resume$ = createEffect(() => {
return this.platformService.resume().pipe(mapTo(PlatformActions.appResumed()));
});
constructor(private platformService: PlatformService) {
}
这确实很好用,但我真的不知道如何优雅地测试它。通常我们会尝试将我们的 TestingModule 设置得尽可能不具体。 e.G
describe('PlatformEffects', () => {
let effects: PlatformEffects;
let platformServiceSpy: SpyObj<PlatformService>;
beforeEach(() => {
platformServiceSpy = jasmine.createSpyObj({
resume: EMPTY
});
TestBed.configureTestingModule({
providers: [
PlatformEffects,
{
provide: PlatformService,
useValue: platformServiceSpy
}
]
});
effects = TestBed.inject(PlatformEffects);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('resume$', () => {
it('dispatches appResumed action on resume event', (done) => {
// AAA: This test should setup the response from the platformService so the arrange setup is coupled in the test.
});
});
});
遗憾的是,这似乎不起作用,因为效果是在构造函数中创建的,因此我的事件流始终为 EMPTY,即使我覆盖了间谍中的恢复响应也不会触发效果。
现在我可以在 beforeEach 中设置一个默认值(例如 of(undefined)
),但是它在我的测试中并没有真正耦合,我不能使用 AAA 模式。
另一方面,我可能每次都创建一个新的 Effects 实例,但这似乎有点过分了不是吗?
有没有更好的方法? NgRx 用动作流很好地解决了这个问题,我想知道是否有类似的解决方案来处理使用来自 DI 的源的效果。
有关详细信息,请参阅 https://timdeschryver.dev/blog/testing-an-ngrx-project#effects
it('fetch$ dispatches a success action', () => {
// The Effect Actions stream is created by instantiating a new `ActionsSubject`
const actions = new ActionsSubject();
const effects = new CustomersEffects(actions, newCustomerService());
// Subscribe on the effect to catch emitted actions, which are used to assert the effect output
const result: Action[] = [];
effects.fetch$.subscribe((action) => {
result.push(action);
});
const action = customerPageActions.enter({ customerId: '3' });
actions.next(action);
expect(result).toEqual([
customersApiActions.fetchCustomerSuccess(
newCustomer({
id: action.customerId,
}),
),
]);
});
我尝试了更多,认为操作流可能只是一个主题,我可以为我自己的服务方法做同样的事情:
describe('PlatformEffects', () => {
let effects: PlatformEffects;
let platformServiceSpy: SpyObj<PlatformService>;
let resumeSubject: ReplaySubject<any>;
beforeEach(() => {
// create new subject that can be used as an event emitter
resumeSubject = new ReplaySubject<any>();
platformServiceSpy = jasmine.createSpyObj({
resume: resumeSubject.asObservable()
});
TestBed.configureTestingModule({
providers: [
PlatformEffects,
{
provide: PlatformService,
useValue: platformServiceSpy
}
]
});
effects = TestBed.inject(PlatformEffects);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('resume$', () => {
it('dispatches appResumed action on resume event', (done) => {
// emit new test string in resume subject
resumeSubject.next('test');
effects.resume$.subscribe((res) => {
expect(res).toEqual(PlatformActions.appResumed());
done();
});
});
});
});
这适用于这种情况,但一旦我尝试对 returns 承诺此解决方案不再有效的服务方法执行相同操作。