NgRx 存储选择器单元测试

NgRx store selector unit testing

ngOnInit(): void {
        this.format = this.store.select(selectUserSettings).pipe(
          filter(
            (userSettings: UserSettingsState) =>
              Boolean(userSettings) && !userSettings.errors && !userSettings.isLoading && !!userSettings.localizationProfile
          ),
          map((userSettings: UserSettingsState) =>
            userSettings.localizationProfile.dateFormat.toLocaleLowerCase().replace('mm', 'MM')
          ),
          takeUntil(this.destroy)
        );
      }

如何为这个函数编写单元测试?我试图模拟 mockUserSettingsSelector = store.overrideSelector(selectUserSettings, initialState.userSettings); 之类的选择器, 和测试

    describe('OverdueTableComponent', () => {
  let component: OverdueTableComponent;
  let fixture: ComponentFixture<OverdueTableComponent>;
  let store: MockStore;
  const initialState: SettingsState = new SettingsInitialState();
  let mockUserSettingsSelector: MemoizedSelector<SettingsState, UserSettingsState>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [OverdueTableComponent, OverdueEventTypeTranslationComponent],
        imports: [SimplebarAngularModule, StoreModule.forRoot({}), StoreModule.forFeature('settings', settingsReducer)],
        providers: [OverdueTableComponent, OverdueEventTypeTranslationComponent, provideMockStore({ initialState })],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(OverdueTableComponent);
    component = fixture.componentInstance;
    store = TestBed.inject(MockStore);
    mockUserSettingsSelector = store.overrideSelector(selectUserSettings, initialState.userSettings);
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('format should not be null', () => {
    mockUserSettingsSelector.setResult({
      errors: undefined,
      isLoading: false,
      localizationProfile: {
        currency: 'dummy-currency',
        currencySymbol: 'dummy-currency-symbol',
        dateFormat: 'dd/MM/yyyy',
        language: 'English (British)',
        timezone: 'dummy-timezone',
      },
      successfullyUpdated: false,
    });
    store.refreshState();
    fixture.detectChanges();
    component.ngOnInit();
    expect(component.format).not.toBe(null);
  });
});

但没有任何结果。

提前致谢!

您在设置模拟值之前调用 component.ngOnInit(),因此不会设置它。

请注意第一个fixture.detectChanges()也会触发ngOnInit方法。如果这是在 beforeEach 中调用的,您需要在设置完成后将其从那里删除并放入测试用例中(并且您可以删除 ngOnInit 的显式触发器)


编辑:

describe('OverdueTableComponent', () => {
  ...

  beforeEach(
    waitForAsync(() => {
      ...
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(OverdueTableComponent);
    component = fixture.componentInstance;
    store = TestBed.inject(MockStore);
    mockUserSettingsSelector = store.overrideSelector(selectUserSettings, initialState.userSettings);
    // PLEASE REMOVE THIS LINE fixture.detectChanges();
  });

  it('should create', () => {
    fixture.detectChanges(); // ADD THE CHANGE DETECTION HERE
    expect(component).toBeTruthy();
  });

  it('format should not be null', () => {
    mockUserSettingsSelector.setResult({
      errors: undefined,
      isLoading: false,
      localizationProfile: {
        currency: 'dummy-currency',
        currencySymbol: 'dummy-currency-symbol',
        dateFormat: 'dd/MM/yyyy',
        language: 'English (British)',
        timezone: 'dummy-timezone',
      },
      successfullyUpdated: false,
    });
    store.refreshState();
    fixture.detectChanges();
    // REMOVE THIS LINE as fixture.detectChanges() does it for you 
    // component.ngOnInit();
    expect(component.format).not.toBe(null);
  });
});

未覆盖的行: