繁体   English   中英

Angular 异步 pipe 在单元测试中未正确更新

[英]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 有关。

我的尝试:

  1. 添加fixture.detectChanges(); - 在beforeEach()it的测试用例中几乎每隔一行(如此极端)都添加了这一行,但没有任何改变
  2. component.plans$ = of([ { name: 'name' } as any ]);硬编码it的测试用例中(我想知道这是否与 Store/MockStore 有关,但即使是硬编码的值似乎在 HTML 中也不起作用)
  3. 使用fixture.whenRenderingDone().then(async () => { <code> }); 在整个测试用例中(也许 HTML 没有在console.log()行出现时呈现)
  4. 与第三个类似,我也尝试了setTimeout() ,理由相同

我的其他想法也是:

  1. 我错过了declarationsimports等方面的内容?
  2. MockStore/Store 没有正确触发对async管道的更改(尽管它们适用于subscribe()

如果有什么遗漏,请告诉我。 先感谢您。

对我来说奇怪的是,您同时拥有storemockStore的句柄。

我认为你应该只使用一个。 我对 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM