[英]Angular Async pipe does not properly update in Unit Tests
在编写单元测试时,我遇到了通过 HTML 中的async
pipe 渲染更新 Observables 的问题。
这个想法是,我不仅要测试组件,还要测试子组件是否都被渲染并具有正确的输入。
这是发生问题的最小示例:
<ng-container *ngFor="let plan of plans$ | async">
<child-component [plan]="plan"></child-component>
</ng-container>
Visible plans: {{ plans$ | async | json }}
组件的最小示例:
export class RecommendationsComponent implements OnInit {
public plans$: Observable<Plan[]>;
constructor(private readonly _store: Store<State>) {
this.plans$ = this._store.pipe(select(selectRecommendationsPayload));
}
public ngOnInit(): void {
this.getRecommendations(); // Action dispatch, state is filled with data
}
}
此模块/组件的单元测试:
describe('Recommendations', () => {
let component: RecommendationsComponent;
let fixture: ComponentFixture<RecommendationsComponent>;
let store: Store<any>;
let mockStore: MockStore<any>;
let actions$: ReplaySubject<any> = new ReplaySubject<any>();
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [RecommendationsComponent],
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule],
providers: [
MockStore,
provideMockStore({ initialState: initialStateMock }),
provideMockActions(() => actions$),
],
});
store = TestBed.inject(Store);
mockStore = TestBed.inject(MockStore);
fixture = TestBed.createComponent(RecommendationsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should successfully retrieve and handle plans', () => {
recommendationsService.getRecommendations = jasmine.createSpy().and.returnValue(of(plans)); // Mock BE response with non-empty data
component.plans$.subscribe(plans => {
console.log(plans);
console.log(fixture.debugElement);
// A few expect() based on state and HTML...
// This fires since all logic starts on ngOnInit() lifecycle
});
});
});
而真正的代码和console.log(plans);
在单元测试中显示正确的数据,出于某种原因plans$ | async
plans$ | async
中的异步始终具有默认 state。 该问题仅与 HTML 有关。
我的尝试:
fixture.detectChanges();
- 在beforeEach()
和it
的测试用例中几乎每隔一行(如此极端)都添加了这一行,但没有任何改变component.plans$ = of([ { name: 'name' } as any ]);
硬编码在it
的测试用例中(我想知道这是否与 Store/MockStore 有关,但即使是硬编码的值似乎在 HTML 中也不起作用)fixture.whenRenderingDone().then(async () => { <code> });
在整个测试用例中(也许 HTML 没有在console.log()
行出现时呈现)setTimeout()
,理由相同我的其他想法也是:
declarations
、 imports
等方面的内容?async
管道的更改(尽管它们适用于subscribe()
)如果有什么遗漏,请告诉我。 先感谢您。
对我来说奇怪的是,您同时拥有store
和mockStore
的句柄。
我认为你应该只使用一个。 我对 mockStore 没有太多经验,所以我会尝试实际的商店。 尝试按照此处所示进行integration testing
。 通过集成测试,我们有实际的商店而不是模拟商店。
describe('Recommendations', () => {
let component: RecommendationsComponent;
let fixture: ComponentFixture<RecommendationsComponent>;
let store: Store<any>;
let actions$: ReplaySubject<any> = new ReplaySubject<any>();
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [RecommendationsComponent],
imports: [
RouterTestingModule.withRoutes([]),
HttpClientTestingModule,
StoreModule.forRoot({
// Pay attention here, make sure this is provided in a way
// where your selectors will work (make sure the structure is
// good)
recommendations: recommendationsReducer,
})
],
});
store = TestBed.inject(Store);
// load the recommendations into the store by dispatching
store.dispatch(new loadRecommendations([]));
fixture = TestBed.createComponent(RecommendationsComponent);
component = fixture.componentInstance;
// see your state here, make sure the selector works
store.subscribe(state => console.log(state));
// any time you want to change plans, do another dispatch
store.dispatch(new loadRecommendations([/* add stuff here */]));
// the following above should make plans$ emit every time
fixture.detectChanges();
});
// !! -- The rest is up to you from now on but what I presented above
// should help in getting new plans$ with the async pipe !!-
it('should successfully retrieve and handle plans', () => {
recommendationsService.getRecommendations = jasmine.createSpy().and.returnValue(of(plans)); // Mock BE response with non-empty data
component.plans$.subscribe(plans => {
console.log(plans);
console.log(fixture.debugElement);
// A few expect() based on state and HTML...
// This fires since all logic starts on ngOnInit() lifecycle
});
});
});
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.