在销毁对象后,Ember无法调用writableChainWatchers

时间:2017-03-08 19:23:23

标签: testing ember.js qunit acceptance-testing ember-qunit

我在Ember测试中得到了一些模糊的错误消息,没有任何有用的堆栈跟踪或任何其他信息来找出根本原因,但是这个案例将奖项视为最难以理解的。< / p>

我有这个Ember应用程序,测试以一种非常奇怪的方式失败:

$ ember test -m "Acceptance | main report"                                                                                                                                                                1 ↵
cleaning up...
Built project successfully. Stored in "/home/username/work/my-project/frontend/tmp/core_object-tests_dist-HinFNKHW.tmp".
ok 1 PhantomJS 2.1 - Acceptance | main report: visiting main home
not ok 2 PhantomJS 2.1 - Acceptance | main report: it changes structure
    ---
        actual: >
            false
        expected: >
            true
        stack: >
            exception@http://localhost:7357/!/assets/test-support.js:7199:49
            adapterDispatch@http://localhost:7357/!/assets/js/vendor.js:54101:22
            dispatchError@http://localhost:7357/!/assets/js/vendor.js:32467:23
            onerrorDefault@http://localhost:7357/!/assets/js/vendor.js:46036:32
            trigger@http://localhost:7357/!/assets/js/vendor.js:73035:19
            http://localhost:7357/!/assets/js/vendor.js:73935:28
            invoke@http://localhost:7357/!/assets/js/vendor.js:15192:18
            flush@http://localhost:7357/!/assets/js/vendor.js:15260:15
            flush@http://localhost:7357/!/assets/js/vendor.js:15384:20
            end@http://localhost:7357/!/assets/js/vendor.js:15454:28
            run@http://localhost:7357/!/assets/js/vendor.js:15577:19
            http://localhost:7357/!/assets/js/vendor.js:15873:29
        message: >
            Error: Assertion Failed: Cannot call writableChainWatchers after the object is destroyed.
        Log: |
    ...

1..2
# tests 2
# pass  1
# skip  0
# fail  1
Not all tests passed.
Error: Not all tests passed.
    at EventEmitter.App.getExitCode (/home/username/work/my-project/frontend/node_modules/testem/lib/app.js:434:15)
    at EventEmitter.App.exit (/home/username/work/my-project/frontend/node_modules/testem/lib/app.js:189:23)
    at /home/username/work/my-project/frontend/node_modules/testem/lib/app.js:103:14
    at tryCatcher (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/promise.js:510:31)
    at Promise._settlePromise (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/promise.js:567:18)
    at Promise._settlePromise0 (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/promise.js:612:10)
    at Promise._settlePromises (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/promise.js:691:18)
    at Async._drainQueue (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/home/username/work/my-project/frontend/node_modules/testem/node_modules/bluebird/js/release/async.js:17:14)
    at processImmediate [as _immediateCallback] (timers.js:383:17)

请注意错误消息:

Cannot call writableChainWatchers after the object is destroyed.

我的第一个问题是,我怎样才能弄清楚这个奇怪错误的原因是什么?当我得到的堆栈跟踪是有帮助的时。获取构建的vendor.js的这些疯狂行号不仅无益,而且堆栈跟踪中的任何内容都不会来自我的应用程序本身。所以没有可以帮助我的行号。

此外,在dev或生产环境中运行应用程序时不会出现此错误情况。 它刚刚在测试环境中发生。

最后,也许更重要:我意识到错误的原因与状态从一个测试泄漏到另一个测试有关。在该测试文件中,您可以看到有两个测试。第一个通过,但第二个通过。当我评论第一个,专注于唯一的失败测试时,事实证明它不会失败。当我在文件中交换这两个测试的顺序时,那个失败的那个,按顺序排在第二位,现在它是第一个没有失败的。相反,当它第一次通过时,它现在在最后一次失败。

更新

我有关于此错误发生的条件的更多信息。

我确定了我的应用程序中的代码行,它在测试中提升了它。它看起来像这样:

Ember.run(() => this.set('value', value));

这行代码位于我的应用使用的compute Ember.Helper方法中。当服务中的某些属性更改其值时,此助手具有一个调用this.recompute()的观察者。很像显示here

引发此错误的实际代码不是this.set调用。我删除了它,代码仍然出现。它仅仅是Ember.run罪魁祸首的召唤。但是,当我直接调用this.set而不将其包装在Ember.run中时,也会发生错误。要完全清楚,以下三行代码中的任何一行都会引发相同的错误:

Ember.run(() => this.set('value', value));
Ember.run(() => {});
this.set('value', value);

我现在唯一能解决的问题是将该行包裹在try/catch内,并使用空的catch块,默默地忽略错误。

我还跟踪了引发此错误的ember.js中的代码行。你可以看一下here。我仍然希望有人能够解决这个问题。

1 个答案:

答案 0 :(得分:3)

这可能是因为您的应用程序的一部分异步执行某些操作,然后页面在完成之前被重新路由。经典的例子是拥有一个加载记录的组件,并在完成后自行设置属性。由于网络呼叫通常很快,您通常不会在生产/本地上看到此信息。但是,如果您足够快以单击指向其他页面的链接,那么您的ember应用程序会抱怨该组件在被修改之前已被销毁(在组件生命周期中销毁该组件后会触发回调)。更频繁地出现在测试中的原因是应用程序通常在页面之间转换得非常快,并且它足够快以创建一个共同的竞争条件。

解决这个问题的最佳工具是插件“ember-concurrency”。您可以选择在设置值之前检查组件是否已被销毁,但这被认为是反模式。追踪导致特定测试失败的原因可能很棘手,但通常将调试器语句放入回调中会有所帮助;如果调试器在错误的测试中被击中,那么你就有罪魁祸首。

Ember concurrency

Useful post explaining why ember concurrency exists