如何在Angular组件中测试订阅的副作用



我有一个Angular组件,MyComponent,它调用一个返回Observable的方法并订阅它,类似

export class MyComponent {
someProperty;
constructor(service: MyService) {}
someButtonClicked() {
this.service.doStuffAsync().subscribe(
resp => this.someProperty = resp;
);
}
}
@Injectable()
export MyService {
doStuffAsync() {
// returns an Observable which notifies asychronously, e.g. like HttoClient.get(url)
}
}

我想测试方法someButtonClicked(),因此我创建了一个MyServiceMock类,并将其注入测试

export class MyServiceMock {
doStuffAsync() {
return of({// some object}).pipe(observeOn(asyncScheduler));
}
}

无论出于何种原因,我都希望MyServiceMockdoStuffAsync()是异步的,因此使用observeOn(asyncScheduler)

但在这一点上,我不知道如何测试someButtonClicked()。我尝试过不同的策略,例如以下

beforeEach(async(() => {
TestBed.configureTestingModule({
providers: [
{ provide: MyService, useClass: MyServiceMock },
]
}).compileComponents();
}));
let fixture: ComponentFixture<MyComponent>;
let component: MyComponent;
beforeEach(() => {
fixture = TestBed.createComponent(MyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test someButtonClicked', async(() => {
component.someButtonClicked();
fixture.whenStable().then(() => {
expect(component.someProperty).toBeDefined();
});
}));

但由于MyServiceMock的CCD_ 5是异步的。

因此,我的问题是,测试订阅异步Observable的方法引起的副作用(即someProperty设置正确(的最佳策略是什么。

// All code should be synchronous within this it block
it('test someButtonClicked', async(() => {
// sync - button clicks are synchronous
component.someButtonClicked();
// but after btn clicks, we need to let some async code to finish
// like rxjs subscriptions etc.
await fixture.whenStable();
// and then continue
expect(component.someProperty).toBeDefined();
}));

不使用fixture.whenStable().then()

我使用一个名为发射的辅助函数

export const emitted = obs$ => new Promise(function(resolve, reject) {
const emitted$ = new Subject();
obs$.pipe(takeUntil(emitted$)).subscribe(
_ => {
emitted$.next();
emitted$.complete();
resolve(true);
},
_ => {
emitted$.next();
emitted$.complete();
reject('An error occurred');
}
)
});

如果您的模拟服务与组件共享相同的可观察实例

export class MyServiceMock {
private obs$ = of({// some object}).pipe(observeOn(asyncScheduler));
doStuffAsync() {
return obs$;
}
}

在你的规格内,你可以等待它发出

it('test someButtonClicked', async(async () => {
component.someButtonClicked();
const service = TestBed.get(MyService);
await emitted(service.doStuffAsync());
expect(component.someProperty).toBeDefined();
}));

看到您订阅了与组件相同的observable实例,您就知道组件已经运行了它的observator函数,就像您在组件之后订阅的一样。