如何跟踪量角器等待的异步任务?

时间:2018-02-05 16:28:38

标签: angular protractor zonejs

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

我很确定超时发生是因为量角器等待某个异步任务。我知道区域,我试图将所有长时间运行的异步任务保留在ngZone之外(根据the FAQ),但由于某种原因,量角器仍然超时。

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

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

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

  

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

     

在使用定位器等待元素时 - 定位器:按(css选择器,[data-e2e ='滚动到商品'])

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

4 个答案:

答案 0 :(得分:12)

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

相对简单的解决方案

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

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

一旦遇到断点,请转到task.callback.[[FunctionLocation]],这将带您(最可能是)一些setTimeoutsetInterval
要对其进行修复,请运行outside of 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并在调用this._updateTaskCount(task,1)之前添加以下内容:

    task.id = counter++;
    this._tasks[task.id] = task;
    
  6. 查找Zone.prototype.scheduleTask,并在调用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区域之外运行进行修复。

答案 1 :(得分:1)

跟踪量角器的异步任务没有特别的方法。您可能会做的是调查哪些异步任务可能会阻止您的流量。如果您正在测试,则有两个主要的调试提示:应用程序代码中的异步任务和测试中的异步任务。

在你的测试中,如果你使用像Jasmine这样的东西,你是否尝试过改变export function getCurrUser(window,user) { ... _req.onsuccess=function(event){ let cursor=event.target.result; //confirm in this line that user object does has some value if(cursor) {user = Object.assign({}, cursor);return;} else {return;} }; ... } 之类的东西?

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

答案 2 :(得分:1)

JeB在建议他的way方面做得很好(某种解决方案总比没有好。)实际上,有一种方法可以获取未修补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实例并像
  3. 一样使用它
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);

答案 3 :(得分:0)

berkov,

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

我添加了import 'zone.js/dist/task-tracking';到polyfills.ts和下面的代码到AppModule.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);

如果可能,请提供任何可行的示例。那将是您的极大帮助。

谢谢

Jignesh Raval

CC:@ hodossy-szabolcs