不能等到 DOM 渲染在 Angular/Jasmine 单元测试中完成

BuZ*_*uZz 2 jasmine d3.js vega angular angular9

我有一个通过 VegaEmbed ( https://github.com/vega/vega-embed )构建的 Angular 饼图组件,它使用 Vega 和 D3 作为底层图形依赖项。它通过提供标题和一些(键,值)对来呈现。我隔离了该组件,并修改了 main.ts 以从 Stackblitz 中运行 Jasmine 与您分享。在这个测试中,我正在检查饼图是否确实<text>为值“30%”呈现了 SVG标签 | “70%”与传奇“联合CEO/主席”| “单独的首席执行官/主席”。但是,它们似乎运行得太早了,而且 VegaEmbed+Vega+D3 仍在忙于构建 SVG。(我仅通过 Chrome 开发工具查看 DOM 就推断出要测试的内容)。

在此处输入图片说明

https://stackblitz.com/edit/angular-d3-pie-chart-unit-test

我已经尝试了一系列的事情:async, FakeAsync+ tick, jasmine.clock, 改变我的 Angular 组件中的 promises 逻辑等等......fixture.whenStable让我更近了一步,但texts声明的第 50 行仍然未定义。

我不知道 Vega、VegaEmbed 和 D3 的内部是如何工作的。如果这些库没有使用承诺,而是使用老式的回调,那么 Angular 的 Zones 可能无法在async?

让我有点困惑的是,console.log(texts);最终在控制台中显示了 4 个文本 SVG 元素的集合。却console.log(texts.length);显示0!

  1. 怎么可能 ?
  2. 我如何让我的测试代码等到 D3 完成绘制 SVG 的那一刻,expect然后才运行语句?

Ali*_*F50 5

这是一个很好的问题,我有类似的问题Ag-Grid,我必须等待渲染或它的回调完成之前,我做的断言,有没有什么好办法像你提到的fakeAsyncasync/done等至少没有,我已经找到。

我发现的一种方法是制作一个实用函数,如下所示:

import { interval } from 'rxjs';
.....
export const waitUntil = async (untilTruthy: Function): Promise<boolean> => {
  while (!untilTruthy()) {
    await interval(25).pipe(take(1)).toPromise();
  }
  return Promise.resolve(true);
};
Run Code Online (Sandbox Code Playgroud)

waitUntil将每 25 毫秒循环一次,直到提供的回调函数为真。时间长短取决于你。

因此,在您的测试中,您可以执行以下操作:

it('should render the chart', async(done) => {
  // make your arrangements
  // do your action
  fixture.detectChanges();
  // wait for promises to resolve (optional)
  await fixture.whenStable();
  await waitUntil(() => /* put a condition here that will resolve to a truthy value 
  at a later time where the rest of the assertions rely on 
  it such as the graph being present with its labels*/);
  // the rest of your assertions of what should be there what should not
  done(); // call done to let the test this async test is done
});
Run Code Online (Sandbox Code Playgroud)

您提到setTimeout使用 0 值工作。这是有效的,因为我们将里面的内容放在setTimeout调用堆栈队列的末尾,因为它是异步运行的。这样做仍然很好,但我喜欢用这种waitUntil方法读取测试的方式。