2017-03-08 77 views
1

我在Ember测试中发现了一些不明确的错误消息,没有有用的堆栈跟踪或任何其他信息来找出潜在的原因,但是这种情况需要奖品作为其中最隐晦的就是这些。Ember无法在对象销毁后调用writableChainWatchers

我有这样的灰烬应用,在那里测试是在一个非常奇怪的方式失败:

$ 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: > 
      [email protected]://localhost:7357/!/assets/test-support.js:7199:49 
      [email protected]://localhost:7357/!/assets/js/vendor.js:54101:22 
      [email protected]://localhost:7357/!/assets/js/vendor.js:32467:23 
      [email protected]://localhost:7357/!/assets/js/vendor.js:46036:32 
      [email protected]://localhost:7357/!/assets/js/vendor.js:73035:19 
      http://localhost:7357/!/assets/js/vendor.js:73935:28 
      [email protected]://localhost:7357/!/assets/js/vendor.js:15192:18 
      [email protected]://localhost:7357/!/assets/js/vendor.js:15260:15 
      [email protected]://localhost:7357/!/assets/js/vendor.js:15384:20 
      [email protected]://localhost:7357/!/assets/js/vendor.js:15454:28 
      [email protected]://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方法内。这位助手的观察员在服务中的某些属性更改其值时调用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。我仍然希望有人能够弄清楚这一点。

回答

3

这可能是由于您的应用程序的某些部分异步执行某些操作,然后页面在完成之前被重新路由。经典的例子是有一个组件加载一条记录并在完成时设置一个属性。由于网络电话通常很快,您通常不会在生产/本地上看到这一点。然而,如果你足够快地点击指向不同页面的链接,你的烬宝应用程序会抱怨组件在被修改之前就被销毁了(在组件在生命周期中被销毁后被触发)。更频繁出现在测试中的原因是应用程序通常在页面之间快速转换,并且创建一个通用竞争条件足够快。

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

Ember concurrency

Useful post explaining why ember concurrency exists

+0

感谢AlexMA。我会更深入地探讨你提到的这个附加组件。我知道你在第一段中提到的这个问题,但是在这种情况下并不完全相同,原因有两个:首先,当它是在一个被破坏的对象中设置值时,错误将会像“yo called”集合'在一个被毁坏的物体上“。但是这个“可写的观察者”事物使得它更加怪异和低层次。其次,无论事情发展得有多快,一些国家从一次测试漏到另一次测试是不可接受的。如果测试通过隔离运行时,它应该在套件中运行时通过。 – Ernesto

+0

我在上面添加了更多相关信息,以防情况下对诊断这种情况有用。 – Ernesto

+0

如果您尝试在'if(!this.isDestroyed){...}'中包装代码,该怎么办?或者试着在那里包装'recompute'电话?在这一点上,我有点超出我的深度,并会在我的开发控制台中打开“暂停捕捉异常”,或尝试在Ember松弛通道中找到一些帮助。 – AlexMA