3

我正在使用 Angular2.0.1 并试图围绕一个带有一些异步任务的角度组件编写单元测试。我会说,这是一件相当普遍的事情。甚至他们最新的测试示例也包括此类异步测试(请参阅此处)。

但是,我自己的测试永远不会成功,总是以消息失败

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

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

这是一些非常简单的代码来重现该问题:

测试组件.html:

{{someDate | amTimeAgo}}

测试组件.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() {
    }
}

测试模块.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);            
        });
    }));

});

有谁知道如何成功测试这个?我可以以某种方式杀死 afterEach 中的所有“剩余”超时吗?或者我可以以某种方式重置异步代码运行的区域以摆脱这个问题吗?

有没有其他人遇到过这个或知道如何成功测试这个?任何提示将不胜感激。

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

虽然 destroy 建议很棒并且有助于解决简单测试中的问题,但我的实际测试包含以下语句以确保嵌套的 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()promise 永远不会得到解决,因此我的断言代码永远不会执行,并且测试仍然会以相同的超时失败。

因此,即使破坏建议适用于给定的简化测试,它也不能让我修复实际测试。

谢谢

4

1 回答 1

3

供参考: 这是有问题的问题管道

我认为问题在于组件永远不会在async区域中被销毁,而有挂起的异步任务,在这种情况下是管道。所以管道ngOnDestroy(消除超时)永远不会被调用,超时被挂起,这让区域等待。

有几件事可以使它起作用:

  1. 我不知道您的组件中还有什么,但就您所展示的内容而言,测试不需要使用async. 它这样做的唯一原因是因为您正在从您的createComponent方法中返回一个承诺。如果您忘记了承诺(或者只是在没有thening 的情况下调用该方法),那么测试将是同步的并且不需要. 测试完成后组件被销毁。测试通过。async

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

     fixture.destroy();
    

    每个人都很高兴!

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


更新

因此,针对这种特殊情况的商定解决方案是模拟管道。管道不会影响组件的任何行为,所以我们不应该真正关心它的作用,因为它只是用于显示。管道本身已经被库的作者测试过了,所以我们不需要在我们的组件中测试它的行为。

@Pipe({
  name: 'amTimeAgo'
})
class MockTimeAgoPipe implements PipeTransform {
  transform(date: Date) {
    return date.toString();
  }
}

然后只需MomentModuleTestBed配置中取出 ,并将 添加MockTimeAgoPipedeclarations

于 2016-10-05T10:14:37.007 回答