Angular Jasmine 测试未在 ngOnInit 中触发订阅
Posted
技术标签:
【中文标题】Angular Jasmine 测试未在 ngOnInit 中触发订阅【英文标题】:Angular Jasmine test not firing subscribe inside ngOnInit 【发布时间】:2019-04-16 20:53:13 【问题描述】:我有一个基本的组件类,在 ngOnInit 里面它订阅一个服务调用
ngOnInit()
this.activityService.getActivities().subscribe(activities =>
console.log('inside sub');
this.recentActivities = activities;
);
所以我包含了这个 console.log 来查看这个订阅是否曾经执行过。
在我的测试中,我 spyOn 这个活动服务方法返回一个我需要的数据的 Observable 集合。然后在测试中,我完成了整个 fixture.detectChanges() 处理,并期望我的数组长度为 1。
fit('should populate recent activities', () =>
const activities = [new ActivityBuilder()
.withId('1').withRecentActivityActionId(RecentActivityActionType.Created).build()
];
spyOn(activityService, 'getActivities').and.returnValue(of(activities));
fixture.detectChanges();
fixture.whenStable().then(() =>
fixture.detectChanges();
);
fixture.detectChanges();
expect(component.recentActivities.length).toBe(1);
);
根据 Angular 文档,他们几乎做同样的事情,我什至尝试了 fakeAsync 方法,但无济于事。我在这里想念什么?为什么订阅块不执行?
【问题讨论】:
请包含 .spec 文件的所有相关部分,包括设置 TestBed 的位置、影响此规范的任何 beforeEach() 函数以及声明的变量。更好的是链接到stackblitz。尝试根据您到目前为止所展示的内容来获取赃物,我猜您还没有掌握实际注入到正在测试的组件中的实际activityService
。
查看完整的上下文对于提供更准确的推荐非常有价值。一旦您通过了测试规范,您可能可以删除您的fixture.detectChanges()
的一些实例。初始调用会触发您需要的 ngOnInit
行为。但是,任何对fixture.detectChanges
的额外调用仅在您打算对呈现的 html 进行断言时才相关(默认情况下,测试规范中的更改检测是关闭的)。
【参考方案1】:
在尝试 fakeAsync 几个小时、为服务提供模拟类、各种 tick() 调用以及许多其他帖子和略有不同的方法之后,罪魁祸首归结为添加了一行:component.ngOnInit();
。
显然,虽然 fixture.detectChanges()
肯定会调用 ngOnInit,但它以某种方式保持了可观察到的冷态。当我调用 fixture.detectChanges()
时,它不会进入 observable 的订阅。一旦我添加了component.ngOnInit()
,observable 就变得很热,并且订阅中的代码被执行,我可以看到我的控制台日志正在打印。奇怪的是,这使得下面的fixture.detectChanges()
调用也跳转到每个后续调用的订阅代码中。
这是我的规范文件的简化版本,希望这可以帮助将来遇到同样问题的任何人。 fakeAsync 和同步区域仍然通过了测试,我以为我需要在 fakeAsync 区域内至少有一个 tick(),但没有必要。
describe('RecentActivityComponent', () =>
let component: RecentActivityComponent;
let fixture: ComponentFixture<RecentActivityComponent>;
let activityService: ActivityService;
beforeEach(async(() =>
TestBed.configureTestingModule(
imports: [AccordionModule, FontAwesomeModule, HttpClientTestingModule],
declarations: [ RecentActivityComponent ],
providers: [AccordionConfig, ActivityService]
).compileComponents();
));
beforeEach(async () =>
fixture = TestBed.createComponent(RecentActivityComponent);
component = fixture.componentInstance;
activityService = TestBed.get(ActivityService);
);
it('should populate recent activities', fakeAsync( () =>
const activities = [new ActivityBuilder()
.withId('1').withRecentActivityActionId(RecentActivityActionType.Created).build()
];
spyOn(activityService, 'getActivities').and.returnValue(of(activities));
component.ngOnInit();
expect(component.recentActivities.length).toBe(1);
));
it('should populate recent activities', () =>
const activities = [new ActivityBuilder()
.withId('1').withRecentActivityActionId(RecentActivityActionType.Created).build()
];
spyOn(activityService, 'getActivities').and.returnValue(of(activities));
component.ngOnInit();
expect(component.recentActivities.length).toBe(1);
);
);
【讨论】:
我很高兴你能够得到一些工作。这永远是第一步!关于你的热/冷可观察结论,我有些不熟悉(如果你有它的来源,你能分享吗?)。我使用Angular StackBlitz 组装了一个相当不错的测试工具,看看是否可以使用fixture.detectChanges
触发ngOnInit
生命周期钩子,同时还可以触发Observable 上的subscribe
块。以上是关于Angular Jasmine 测试未在 ngOnInit 中触发订阅的主要内容,如果未能解决你的问题,请参考以下文章
通过 Webpack(终端)执行 Typescript Jasmine 测试以测试 Angular2
Angular 1.x Browserify Jasmine 如何设置代码覆盖测试?
typescript Angular 2+ Jasmine测试规格样板
如何使用 Jasmine 为私有方法编写 Angular / TypeScript 单元测试