期望 jasmine Spy 在超时前被调用 "eventually"

Expect jasmine Spy to be called "eventually", before timeout

我最近写了很多异步单元测试,结合使用 Angular 的 fakeAsync,从 async 测试主体函数返回 Promises,Jasmine done 回调等。一般来说,我已经能够让一切以完全确定的方式工作。

我的代码的一些部分以非常复杂且难以模拟的 3rd 方库以非常复杂的方式交互。我想不出一种方法来挂钩事件或生成保证在该库完成后台工作后解决的承诺,所以目前我的测试卡在使用 setTimeout:

class MyService {
  public async init() {
    // Assume library interaction is a lot more complicated to replace with a mock than this would be
    this.libraryObject.onError.addEventListener(err => {
      this.bannerService.open("Load failed!" + err);
    });
    // Makes some network calls, etc, that I have no control over
    this.libraryObject.loadData();
  }
}

it("shows a banner on network error", async done => {
    setupLibraryForFailure();
    await instance.init();

    setTimeout(() => {
        expect(banner.open).toHaveBeenCalled();
        done();
    }, 500);  // 500ms is generally enough... on my machine, probably
});

这让我很紧张,尤其是 setTimeout 中的幻数。它的扩展性也很差,因为我确信 500 毫秒比我完成的任何其他测试都要长得多。

我想我想做的是能够告诉 Jasmine 轮询 banner.open 间谍直到它被调用,或者直到测试超时结束并且测试失败。然后,一旦错误处理程序被触发并完成,测试就会注意到。有没有更好的方法,或者这是一个好主意?它是某个地方的内置模式我没有看到吗?

我认为您可以利用 callFake,基本上是在调用此函数后调用另一个函数。

像这样:

it("shows a banner on network error", async done => {
    setupLibraryForFailure();
    // maybe you have already spied on banner open so you have to assign the previous
    // spy to a variable and use that variable for the callFake
    spyOn(banner, 'open').and.callFake((arg: string) => {
      expect(banner.open).toHaveBeenCalled(); // maybe not needed because we are already doing callFake
      done(); // call done to let Jasmine know you're done
    });
    await instance.init();
});

我们正在 banner.open 上设置一个间谍,当它被调用时,它将使用 callFake 调用回调函数,我们在这个回调函数中调用 done 让 Jasmine 知道我们已经完成了我们的断言。