[英]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.