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 单元测试

如何在 Angular 中使用 Jasmine 测试 RxJS switchMap?

为使用 Observables 的 Angular 2 组件编写 Jasmine 测试