簡體   English   中英

Angular 單元測試使用 TestBed.inject 根據測試執行順序成功/失敗

[英]Angular Unit Tests using TestBed.inject succeed/fail according to tests execution order

我有一個使用 Jest 進行單元測試的 angular 應用程序(v11.1.0)。

我使用TestBed.inject來獲取單個測試中的服務實例並 spyOn 他們的方法進行測試,或者他們已被調用或 mocking 返回值。

但是切換到 Typescript strict模式后,測試失敗。 但是如果我改變一些測試的順序,一切都會順利進行。 似乎模擬的服務仍然在不同的單元測試之間進行交互。

我嘗試使用jest.resetAllMocks() ,但它也沒有解決問題。 在我使用的代碼下方:

單元測試

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        imports: [
          RouterTestingModule,
          HttpClientTestingModule,
          TranslateModule.forRoot(),
        ],
        declarations: [AppComponent],
        providers: [
          { provide: InformationService, useValue: informationServiceMock }
        ],
      }).compileComponents();
    })
  );

  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
  });

 describe('Test set', () => {
    it(`should have default text`, () => {
      fixture.detectChanges();
      expect(component.maintenanceBannerMessage).toBe('BANNER DE');
      expect(component.maintenanceBannerTitle).toBe('TITLE DE');
    });

    it(`Should open function`, () => {
      const dialog = TestBed.inject(MatDialog);
      const informationService = TestBed.inject(InformationService);
      jest.spyOn(informationService, 'updateOverlayAction');
      jest.spyOn(dialog, 'open');
      fixture.detectChanges();

      expect(dialog.open).toHaveBeenCalled();
      expect(informationService.updateOverlayAction).toHaveBeenCalled();
    });

    //------------------------------------------------------------
    // If I move this test in 2. position, the test `Should open function` FAILS
    // as getAnnouncementsByType keeps returning null instead of getting it from the
    // informationServiceMock provided in the configureTestingModule

    it(`should not be showed`, () => {
      const informationService = TestBed.inject(InformationService);
      jest
        .spyOn(informationService, 'getAnnouncementsByType')
        .mockReturnValue(of(null as any));
      fixture.detectChanges();

      expect(component.maintenanceBannerMessage).toBeUndefined();
      expect(component.maintenanceBannerTitle).toBeUndefined();
    });
    //------------------------------------------------------------

    it(`should not be showed`, () => {
      const dialog = TestBed.inject(MatDialog);
     const informationService = TestBed.inject(InformationService);
      jest
        .spyOn(informationService, 'getAnnouncementsByType')
        .mockReturnValue(of(null as any));
      jest.spyOn(dialog, 'open');
      fixture.detectChanges();

      expect(dialog.open).not.toHaveBeenCalled();
    });
  });
});

服務模擬

export const announcementsMockData = [
  {
    announcementId: '6000',
    type: AnnouncementType.BANNER,
    text: { de: 'BANNER DE', fr: 'BANNER FR', it: 'BANNER IT', en: 'BANNER EN' },
    title: { de: 'TITLE DE', fr: 'TITLE FR', it: 'TITLE IT', en: 'TITLE EN' }
  }, {
    announcementId: '6100',
    type: AnnouncementType.OVERLAY,
    text: { de: 'OVERLAY DE', fr: 'OVERLAY FR', it: 'OVERLAY IT', en: 'OVERLAY EN' },
    title: { de: 'OVERLAY DE', fr: 'OVERLAY FR', it: 'OVERLAY IT', en: 'OVERLAY EN' },
    _links: { create: { href: '/announcements/6100/actions' } }
  }
];

export const informationServiceMock = {
  getAnnouncementsByType: (type: AnnouncementType) => {
    return announcementsMockData.map(a => new Announcement(a)).filter(a => a.type === type);
  },
  updateOverlayAction: (link: ResourceLink, action: OverlayAction) => of(null),
};

應用組件

this.informationService.getAnnouncementsByType(AnnouncementType.BANNER)
  .pipe(takeUntil(this.destroy$))
  .subscribe(([currentLanguage, banners]) => {
    if (banners?.length > 0) {
      if (banners[0].title) {
        this.maintenanceBannerTitle = banners[0].title[currentLanguage.key as keyof LanguageObject];
      }
      if (banners[0].text) {
        this.maintenanceBannerMessage =
          banners[0].text[currentLanguage.key as keyof LanguageObject];
      }
    }
  });

測試特定的間諜應該恢復到所有測試通用的一些實現,不這樣做會導致測試交叉污染,因為測試會影響后續測試。

jest.resetAllMocks()提供了不良行為,應該完全避免。 beforeEach中使用時,它會毫無例外地重置所有間諜的實現並使它們成為存根。 在測試中使用時,這也會導致測試交叉污染。 如果需要重置特定的間諜實現,可以使用mockReset()來完成。

根據經驗, jest.restoreAllMocks()應該在beforeEach中使用,它會將使用jest.spyOn創建的所有間諜恢復到原始實現,以防萬一。 這種行為通常適用於所有測試,因此可以在 Jest 配置中啟用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM