24

我有一个中型 Angular 应用程序,由于某些原因,我的一些量角器测试在针对我的实时生产环境运行时超时。

我相当确定超时发生是因为量角器在等待一些异步任务。我知道区域,我试图将所有长时间运行的异步任务排除在 ngZone 之外(根据常见问题解答),但由于某种原因量角器仍然超时。

我可能错过了一些东西,但我不知道如何调试问题。有没有办法找出代码量角器的哪一部分正在等待?

NgZone 只公开函数来确定是否有微任务或宏任务在运行,但没有告诉我是哪一个。

编辑:量角器显示的这种超时错误的典型示例:

失败:等待异步 Angular 任务在 11 秒后完成超时。这可能是因为当前页面不是 Angular 应用程序。有关更多详细信息,请参阅常见问题解答:https ://github.com/angular/protractor/blob/master/docs/timeouts.md#waiting-for-angular

在等待带有定位器的元素时 - 定位器:By(css selector, [data-e2e='scroll-to-offer'])

该元素存在于页面上(我已手动验证),但量角器仍然超时。

4

4 回答 4

27

我有一个类似的问题,可测试性不稳定,我只知道我有一些pendingMacroTasks. 将这些任务本地化确实有点棘手(您不会遍历整个代码库并寻找setTimeout.
但是我最终设法做到了。

比较简单的解决方案

您可以做的第一件事是在 Chrome 中打开您的开发工具,按Ctrl+O,输入zone.js并按Enter
这将打开zone.js源文件(不是 Typescript,而是一些东西)。
zone.js 中查找Zone.prototype.runTask并在此方法中放置一个断点。在你的应用程序加载后
启用断点,否则它会被击中太多次。

如果您的可测试性不稳定,则可能意味着您有一些重复性的宏任务会重新安排自己的时间。至少我的情况是这样。
如果是这种情况,则在加载应用程序后每 X 次都会触发断点。

一旦断点被​​命中,go to task.callback.[[FunctionLocation]]this 将带你到(很可能) some setTimeoutor setInterval
要修复它,请在 Angular zone 之外运行它。

有点棘手的解决方案

这涉及调整 Zone.js 代码,但会为您提供更持久的调试信息。

  1. node_modules/zone.js/dist/zone.js
  2. 寻找 Zone 的构造函数:function Zone(parent, zoneSpec)
  3. 在里面添加以下内容:this._tasks = {}
  4. 在全局范围内添加计数器变量:let counter = 0
  5. Zone.prototype.scheduleTask在调用 to 之前查找并添加以下内容this._updateTaskCount(task,1)

    task.id = counter++;
    this._tasks[task.id] = task;
    
  6. Zone.prototype.scheduleTask在调用 to 之前查找并添加以下内容this._updateTaskCount(task,-1)

    delete this._tasks[task.id]
    
  7. 重新编译应用程序并运行它

假设您进行了上述调整,您始终可以像这样访问待处理的任务:

tasks = Object.values(window.getAllAngularTestabilities()[0]._ngZone._inner._tasks)

要获取影响可测试性状态的所有未决宏任务:

tasks.filter(t => t.type === 'macroTask' && t._state === 'scheduled')

之后,与之前的解决方案类似,检查.callback.[[FunctionLocation]]并通过在 Angular 区域之外运行来修复。

于 2018-08-05T16:56:06.820 回答
12

JeB 在建议他的方式方面做得很好(一些解决方案总比没有好)。事实上,有一种方法可以在不修补zone.js文件的情况下获取待处理任务的列表。我在查看角度来源时得到了这个想法:https ://github.com/angular/angular/blob/master/packages/core/src/zone/ng_zone.ts#L134

以下是如何:

  1. 导入node_modules/zone.js/dist/task-tracking.js文件后zone.js
  2. 然后在您的代码中获取NgZone实例并像使用它一样使用它
import * as _ from "lodash";
...
const ngZone = moduleInstance.injector.get(NgZone);
setInterval(() => {
    var taskTrackingZone = (<any>ngZone)._inner.getZoneWith("TaskTrackingZone");
    if (!taskTrackingZone) {
        throw new Error("'TaskTrackingZone' zone not found! Have you loaded 'node_modules/zone.js/dist/task-tracking.js'?");
    }
    var tasks: any[] = taskTrackingZone._properties.TaskTrackingZone.getTasksFor("macroTask");
    tasks = _.clone(tasks);
    if (_.size(tasks) > 0) {
        console.log("ZONE pending tasks=", tasks);
    }
}, 1000);

示例放在这里:https ://stackblitz.com/edit/zonejs-pending-tasks 。

这个怎么运作:

于 2019-09-09T12:45:49.587 回答
1

没有特定的方法来跟踪量角器的异步任务。您可能要做的是调查哪些异步任务可能会阻塞您的流程。如果您正在测试,则有 2 个主要的调试线索:应用代码中的异步任务和测试中的异步任务。

在您的测试中,如果您使用 Jasmine 之类的东西,您是否尝试过改变之类的东西jasmine.DEFAULT_TIMEOUT_INTERVAL;

无论如何,除了在启动和回调时开始记录所有异步任务之外,我没有看到更好的调试选项。

于 2018-02-13T11:15:04.703 回答
-1

贝尔科夫,

我真的需要您的帮助才能使用您提供的功能获取任务列表。

我补充说import 'zone.js/dist/task-tracking';AppModule.ts的polyfills.ts构造函数方法的代码和下面的代码。但它抛出错误Cannot find name 'moduleInstance'.

// app.module.ts

const ngZone = moduleInstance.injector.get(NgZone);
setInterval(() => {
    var taskTrackingZone = (<any>ngZone)._inner.getZoneWith("TaskTrackingZone");
    if (!taskTrackingZone) {
        throw new Error("'TaskTrackingZone' zone not found! Have you loaded 'node_modules/zone.js/dist/task-tracking.js'?");
    }
    var tasks: any[] = taskTrackingZone._properties.TaskTrackingZone.getTasksFor("macroTask");
    tasks = _.clone(tasks);
    if (_.size(tasks) > 0) {
        console.log("ZONE pending tasks=", tasks);
    }
}, 1000);

如果可能,您能否提供任何工作示例。那将是你很大的帮助。

谢谢,

吉涅什·拉瓦尔

抄送:@hodossy-szabolcs

于 2019-12-10T13:15:53.117 回答