2017-03-08 26 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