简体   繁体   English

Angular 单元测试使用 TestBed.inject 根据测试执行顺序成功/失败

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

I have an angular application (v11.1.0) using Jest for UnitTests.我有一个使用 Jest 进行单元测试的 angular 应用程序(v11.1.0)。

I use TestBed.inject in order to get a service instance inside single tests and spyOn their methods to test or they have been called or mocking the return values.我使用TestBed.inject来获取单个测试中的服务实例并 spyOn 他们的方法进行测试,或者他们已被调用或 mocking 返回值。

However after switching to Typescript strict mode, the test fails.但是切换到 Typescript strict模式后,测试失败。 But if I change the order of some tests, everything runs smoothly.但是如果我改变一些测试的顺序,一切都会顺利进行。 It seems the mocked services still interacts between the different unit tests.似乎模拟的服务仍然在不同的单元测试之间进行交互。

I tried to use jest.resetAllMocks() , but it does not solve the problem either.我尝试使用jest.resetAllMocks() ,但它也没有解决问题。 Below the code I use:在我使用的代码下方:

Unit Test单元测试

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();
    });
  });
});

Service Mock服务模拟

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),
};

app component应用组件

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];
      }
    }
  });

Test-specific spies should be restored to some implementation that is common for all tests, not doing this results in test cross contamination because a test affects subsequent tests.测试特定的间谍应该恢复到所有测试通用的一些实现,不这样做会导致测试交叉污染,因为测试会影响后续测试。

jest.resetAllMocks() provides undesirable behaviour and should be avoided at all. jest.resetAllMocks()提供了不良行为,应该完全避免。 When used in beforeEach , it resets the implementation of all spies with no exception and makes them stubs.beforeEach中使用时,它会毫无例外地重置所有间谍的实现并使它们成为存根。 When used in a test, this results in test cross contamination as well.在测试中使用时,这也会导致测试交叉污染。 In case specific spy implementation needs to be reset, this can be done with mockReset() .如果需要重置特定的间谍实现,可以使用mockReset()来完成。

As a rule of thumb, jest.restoreAllMocks() should be used in beforeEach , it restores all spies that were created with jest.spyOn to original implementation, in case there's any.根据经验, jest.restoreAllMocks()应该在beforeEach中使用,它会将使用jest.spyOn创建的所有间谍恢复到原始实现,以防万一。 This behaviour is generally desirable for all tests so it can be enabled in Jest configuration instead.这种行为通常适用于所有测试,因此可以在 Jest 配置中启用它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Angular TestBed.inject - Angular TestBed.inject Angular 9 TestBed.inject 和提供者覆盖 - Angular 9 TestBed.inject & Provider Overrides Angular 单元测试 - new Service() 和 Testbed.inject() 有什么区别? - Angular Unit Test - What is the difference between new Service() and Testbed.inject()? Angular单元测试中Testbed.inject(serviceName)和fixture.debugElement.injector.get(serviceName)的区别 - Difference between Testbed.inject(serviceName) and fixture.debugElement.injector.get(serviceName) in Angular Unit Testing Angular 4 单元测试 (TestBed) 非常慢 - Angular 4 Unit Tests (TestBed) extremely slow 注入间谍时的 Angular TestBed.inject 类型错误 - Angular TestBed.inject type error when injecting into a spy Angular 6 和 Jest 的测试出错 - TypeError: testing.TestBed.inject is not a function - Error in tests with Angular 6 and Jest - TypeError: testing.TestBed.inject is not a function Angular 单元测试失败,但不是本地 - Angular unit tests fail, but not locally 如何使用 TestBed 和 Jasmine 在 NativeScript 中实现单元测试? - How to implement unit tests in NativeScript using TestBed and Jasmine? Angular 2 单元测试:如何覆盖 TestBed 中的单个提供程序(用于服务的单元测试,而不是具有服务依赖项的组件)? - Angular 2 unit tests: How to override a single provider in TestBed (for unit tests for a service, not a component, with service dependencies)?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM