Angular 2测试 - 异步函数调用 - 何时使用

96

在 Angular 2 中进行测试时,何时使用 TestBed 中的异步函数?

什么时候需要使用它?

 beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [MyModule],
            schemas: [NO_ERRORS_SCHEMA],
        });
    });

那么你什么时候会使用这个呢?

beforeEach(async(() => {
    TestBed.configureTestingModule({
        declarations: [MyModule],
        schemas: [NO_ERRORS_SCHEMA],
    });
}));

有人可以为我解惑吗?

2个回答

107

async会在所有任务完成之前将回调包裹在一个区域中,该区域跟踪所有异步任务(例如setTimeout),并防止下一个测试开始。一旦所有异步任务完成,async就会完成。

如果您曾在Angular之外使用Jasmine,您可能已经看到了传递给回调函数的done

it('..', function(done) {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
    done();
  });
});

这里,这是原生的Jasmine,在这里我们告诉Jasmine这个测试应该延迟完成,直到我们调用done()。如果我们没有调用done()而是做了这件事:

it('..', function() {
  someAsyncAction().then(() => {
    expect(something).toBe(something);
  });
});

因为 Promise 解决异步任务的时间比同步任务完成的时间稍晚,所以测试甚至会在期望之前完成。

使用 Angular(在 Jasmine 环境下),当我们使用 async 时,Angular 实际上会在后台调用 done。它会跟踪 Zone 中的所有异步任务,并在它们全部完成时,在后台调用 done

在您特定的 TestBed 配置中,通常在想要使用 compileComponents 时才会使用它。我很少遇到需要以其他方式调用它的情况。

beforeEach(async(() => {
   TestBed.configureTestingModule({
     declarations: [MyModule],
     schemas: [NO_ERRORS_SCHEMA],
   })
   .compileComponent().then(() => {
      fixture = TestBed.createComponent(TestComponent);
   });
}));

当测试一个使用templateUrl的组件时(如果您没有使用webpack),那么Angular需要发起XHR请求以获取模板,因此组件的编译将是异步的。因此,我们应该等待直到解析完成后再继续测试。


1
很棒的回答@peeskillet。只是为了确保我理解:当您拥有内联模板时,async不是必需的。当您使用templateUrl时,它是必需的。但是,包括async不会“破坏”内联模板组件。您认为可以默认每个测试都使用async吗? - vince
2
@vincecampanale,templateUrl只在beforeEach的配置期间起作用。在这种情况下,您需要调用“compileComponents”。如果您问的是每个测试中是否使用“async”,那么它与此无关。至于何时安全地调用“compileComponents”,请参见When am I supposed to call compileComponents - Paul Samsotha
2
@vincecampanale 并不总是您希望在测试之前调用函数。有时您可能希望在执行一些初始化后再调用它。您需要了解调用它实际上会发生什么。大多数情况下,这应该没问题。但我个人不喜欢他们自作主张做出这个决定。但我看到很多人遇到的问题是忘记调用它,他们想知道为什么某些东西不起作用。因此,也许更好的做法是生成调用。位置可能存在争议,但至少他们调用了它。 - Paul Samsotha
2
一般来说,当您想要视图(重新)渲染时,就应该调用它。例如,创建组件-> 渲染视图。但是,如果您想要先初始化某些内容,比如创建组件-> 更改用于渲染的组件值-> 渲染视图。这就是我所说的可能您想要先初始化某些内容。 - Paul Samsotha
1
哦,还有一件事。第一次调用它是在组件中的 ngOnInit 被调用时。有时在测试时这很重要。 - Paul Samsotha
显示剩余3条评论

27

当您在测试中进行异步调用时,实际测试函数会在异步调用完成之前就已经完成了。如果需要在调用完成后验证某些状态(通常情况下是这样的),那么测试框架将在仍有异步工作正在进行时报告测试已完成。

使用async(...)可以告诉测试框架在返回的Promise或Observable完成之前等待,然后再将测试视为已完成。

it('should show quote after getQuote promise (async)', async(() => {
  fixture.detectChanges();

  fixture.whenStable().then(() => { // wait for async getQuote
    fixture.detectChanges();        // update view with quote
    expect(el.textContent).toBe(testQuote);
  });
}));

将代码传递给then(...)的部分会在测试函数本身完成后执行

使用async()可以让测试框架知道需要等待承诺和可观察对象完成后再将测试视为已完成。

另请参见


1
它变成了https://angular.io/api/core/testing/waitForAsync,可能是因为async现在是一个模糊的名称(Promises)。 - PhiLho

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接