I've had my share of obscure error messages in Ember tests with no useful stack trace or any other information to figure out the underlying cause, but this case takes the prize for being the most obscure of them all.
I have this Ember app where tests are failing in a very bizarre way:
$ 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)
Note the error message:
Cannot call writableChainWatchers after the object is destroyed.
My first question is, how can I figure out what's the cause of this weird error, when the stack trace I get is anything but helpful. Not only is it unhelpful to get these insane line numbers of the built vendor.js
, but also nothing in the stack trace comes from my app itself. So no line number that could help me here.
Also, this error situation does not occur when running the app in dev or production environment. It's just happening in the test environment.
And finally, and perhaps more important: I realized that the cause of the error has something to do with state leaking from one test to another. In that test file you can see there are two tests. The first one passes, but the second one fails. When I commented the first one, to focus in the only failing test, it turns out it does not fail. When I swap the order of these two tests in the file, then the one that was failing, that used to be second in order, now that it is the first one it does not fail. And conversely, the one that was passing when it was first, it's now failing when it comes last.
Update
I have more information about the conditions under which this error occurs.
I pinpointed the line of code in my app that raises it in the tests. It looks like this:
Ember.run(() => this.set('value', value));
This line of code is inside a compute
method in a Ember.Helper
my app uses. This helper has an observer that calls this.recompute()
when some properties in a service change its values. Much like is shown here.
The actual code raising this error is not the this.set
call. I removed it and the code still occurs. It's the mere invocation of Ember.run
the culprit. However, when I invoked this.set
directly without wrapping it in Ember.run
, the error also occurs. To be fully clear, any of the following three lines of code raise the same error:
Ember.run(() => this.set('value', value));
Ember.run(() => {});
this.set('value', value);
The only way I have right now to fix it is to wrap that line within a try/catch
with an empty catch
block, silently ignoring the error.
I also tracked down the line of code in ember.js that is raising this error. You can take a look at it here. I still hope someone will be able to figure this out.