【发布时间】:2021-05-21 07:39:38
【问题描述】:
我有一个使用 Jest for UnitTests 的 Angular 应用程序 (v11.1.0)。
我使用TestBed.inject 来获取单个测试中的服务实例,并监视他们的测试方法,或者他们已被调用或模拟返回值。
但是切换到 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];
}
}
});
【问题讨论】:
-
尚不清楚它与 TS strict 有何关系。它不会影响它在运行时的工作方式。你是说alwaysStrict吗?
-
我也不认为它是相关的。我在重构代码时提到了它,添加了
strict模式和 Angular strictTemplate 检查。似乎与模拟测试有关,因为颠倒了它们运行的顺序,因此它不应该与代码本身有关,而是与在测试中保持不变的先前模拟值有关 -
不清楚在什么时候调用 getAnnouncementsByType。
jest.resetAllMocks()不仅应该尝试,而且应该永久使用。行为是否与它完全相同?将其移至 beforeEach 或更好地在 Jest 配置中启用它。 -
在 2. 测试中,我不模拟
getAnnouncementsByType,因为我想测试“快乐路径”,因此 app.component 中的默认值。该方法在组件中被调用。但是,如果我在 2. 之前运行 3. 测试,getAnnouncementsByType在组件中返回 null。好像测试会“记住”返回null的先前模拟值。在 BeforeEach 中添加jest.resetAllMocks()会在subscribe()方法中触发组件中的异常,因为返回的 observable 是未定义的。所以我暂时把它删了。 -
我明白了。我在没有注意到的情况下粘贴了 resetAllMocks。resetAllMocks 永远不应该在 Jest 中使用,它是有害的。请改用 restoreAllMocks 等。 好像测试会“记住”之前返回 null 的模拟值 - 确实如此,这就是为什么在每次测试之前始终将模拟恢复到原始状态很重要。
标签: angular typescript unit-testing jestjs