setTimeout的Angular2异步测试问题

时间:2016-10-05 07:29:29

标签: angular jasmine angular2-testing

我正在使用Angular2.0.1并尝试使用一些异步任务围绕角度组件编写单元测试。我要说,这是一件相当普遍的事情。甚至他们最新的测试示例也包括这些异步测试(参见here)。

我自己的测试永远不会成功,但总是没有消息

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

长话短说,我花了几个小时来确定问题的实际来源。 我使用的是angular2-moment库,我在那里使用了一个名为amTimeAgo的管道。此管道包含一个window.setTimeout(...),它永远不会被删除。 如果我删除了amTimeAgo管道,测试会成功,否则会失败。

这里有一些非常简单的代码来重现这个问题:

testcomponent.html:

{{someDate | amTimeAgo}}

testcomponent.ts:

import { Component } from "@angular/core";
import * as moment from "moment";

@Component({
    moduleId: module.id,
    templateUrl: "testcomponent.html",
    providers: [],
})
export class TestComponent{
    someDate = moment();

    constructor() {
    }
}

testmodule.ts

import { NgModule }      from "@angular/core";
import {MomentModule} from 'angular2-moment';
import { TestComponent } from './testcomponent';

@NgModule({
    imports: [
        MomentModule,
    ],
    declarations: [
        TestComponent,
    ]
})
export class TestModule {
}

testcomponent.spec.ts:

import { async, TestBed, ComponentFixture } from "@angular/core/testing";
import { TestComponent } from './testcomponent';
import { TestModule } from './testmodule';

let component: TestComponent;
let fixture: ComponentFixture<TestComponent>;

function createComponent() {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;

    fixture.detectChanges();
    return Promise.resolve();
}

describe("TestComponent", () => {
    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [
                TestModule],
        }).compileComponents();
    }));

    it('should load the TestComponent', async(() => {
        createComponent().then(() => {
            expect(component).not.toBe(null);            
        });
    }));

});

有没有人知道如何成功测试这个?我可以以某种方式杀死所有&#34;剩下的&#34;在afterEach中超时?或者我可以以某种方式重置异步代码运行的区域以摆脱这个问题吗?

是否有其他人遇到此问题或知道如何成功测试?任何提示都将不胜感激。

更新 @peeskillet在使用fixture.destroy()暗示解决方案之后,我在实际测试中尝试了这个(这里的示例只是重现问题所需的最小代码)。实际测试包含嵌套的promise,否则我不需要asyncdetectChanges方法。

虽然破坏建议很好并且有助于简单测试中的问题,但我的实际测试包含以下语句以确保正确解析嵌套的promise:

it('should check values after nested promises resolved', async(() => {
    createComponent().then(() => {
        fixture.whenStable().then(() => {
            component.selectedToolAssemblyId = "2ABC100035";

            expect(component.selectedToolAssembly).toBeDefined();
            expect(component.selectedToolAssembly.id).toBe("2ABC100035");

            fixture.destroy();
        });
        fixture.detectChanges();
    });
}));

问题在于,使用页面中的amTimeAgo管道,fixture.whenStable()承诺永远不会被解析,因此我的断言代码永远不会被执行,并且测试仍会以相同的超时失败。

因此,即使销毁建议适用于给定的简化测试,也无法修复实际测试。

由于

1 个答案:

答案 0 :(得分:3)

供参考: here is the problem pipe in question

我认为问题在于,当有待处理的异步任务时,组件永远不会在async区域中被破坏,在这种情况下是管道。因此管道的ngOnDestroy(删除超时)永远不会被调用,超时会挂起,这会使区域等待。

有几件事可以使它发挥作用:

  1. 我不知道你的组件中还有什么,但是根据你所展示的内容,测试并不需要使用{{1 }}。这样做的唯一原因是因为您从async方法返回承诺。如果您忘记了承诺(或只是调用方法而不使用),那么测试将是同步的,不需要createComponent。测试完成后,组件会被破坏。测试通过。

  2. 这是更好的解决方案:只需自行销毁组件!

    async

    每个人都快乐!

  3. 我测试了这两种解决方案,它们都有效。

    更新

    因此,对于这个特殊情况的商定解决方案就是模拟管道。管道不会影响组件的任何行为,因此我们不应该关心它的作用,因为它只是用于显示。管道本身已经由库的作者进行测试,因此我们无需在组件内测试其行为。

    fixture.destroy();
    

    然后从@Pipe({ name: 'amTimeAgo' }) class MockTimeAgoPipe implements PipeTransform { transform(date: Date) { return date.toString(); } } 配置中取出MomentModule,并将TestBed添加到MockTimeAgoPipe