vue-test-utils: "Cannot read property '$options' of undefined" when saving and manipulating data in watchers
Version
1.0.0-beta.29
Reproduction link
https://codesandbox.io/s/20yl7w8y6y
Steps to reproduce
In the Code Sandbox run the tests - they will fail. Due to the way Code Sandbox runs tests if you re-run them they’ll pass the second time. You can make them run properly again by refreshing the page or deleting a semicolon somewhere in the file.
Commenting out either line in the handler function will cause the tests to pass.
What is expected?
Inside watchers, you should be able to save the component data into a variable and then set it.
What is actually happening?
Saving component data in a variable and then setting the data inside a watcher fails tests in the latest version of vue/vue-test-utils. This has caused many tests to suddenly fail in our application.
Stack trace (from CodeSandbox):
Cannot read property '$options' of undefined
TypeError: Cannot read property '$options' of undefined
at updateChildComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2984:8)
at prepatch (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:4388:5)
at patchVnode (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:6180:7)
at VueComponent.patch [as __patch__] (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:6352:9)
at VueComponent.Vue._update (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2815:19)
at VueComponent.updateComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2933:10)
at Watcher.get (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3330:25)
at Watcher.run (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3405:22)
at flushSchedulerQueue (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3163:13)
at queueWatcher (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3252:9)
at Watcher.update (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3395:5)
at Dep.notify (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:740:13)
at Object.reactiveSetter [as myData] (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:1065:11)
at VueComponent.proxySetter [as myData] (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3482:26)
at VueComponent.handler (https://20yl7w8y6y.codesandbox.io/test.spec.js:21:21)
at VueComponent.Vue.$watch (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3797:12)
at createWatcher (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3754:13)
at initWatch (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3736:7)
at initState (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3499:5)
at VueComponent.Vue._init (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:4877:5)
at new VueComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:5024:12)
at createComponentInstanceForVnode (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:4546:10)
at init (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:4377:45)
at createComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:5850:9)
at createElm (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:5797:9)
at VueComponent.patch [as __patch__] (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:6347:7)
at VueComponent.Vue._update (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2812:19)
at VueComponent.updateComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2933:10)
at Watcher.get (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3330:25)
at new Watcher (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:3319:12)
at mountComponent (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:2940:3)
at VueComponent.Vue.$mount (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:8898:10)
at VueComponent.Vue.$mount (https://20yl7w8y6y.codesandbox.io/node_modules/vue/dist/vue.common.dev.js:11692:16)
at mount (https://20yl7w8y6y.codesandbox.io/node_modules/@vue/test-utils/dist/vue-test-utils.js:8649:21)
at shallowMount (https://20yl7w8y6y.codesandbox.io/node_modules/@vue/test-utils/dist/vue-test-utils.js:8677:10)
at Object.eval (https://20yl7w8y6y.codesandbox.io/test.spec.js:34:33)
at https://codesandbox.io/static/js/vendors~sandbox.5596247f.chunk.js:1:180690
at new Promise (<anonymous>)
at t.callAsyncFn (https://codesandbox.io/static/js/vendors~sandbox.5596247f.chunk.js:1:180353)
at https://codesandbox.io/static/js/sandbox.37038f94.js:1:186582
at g (https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:11215)
at Generator._invoke (https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:11003)
at Generator.e.(anonymous function) [as next] (https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:11394)
at r (https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:702)
at u (https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:912)
at https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:971
at new Promise (<anonymous>)
at https://codesandbox.io/static/js/common-sandbox.458a22cf.chunk.js:1:853
at https://codesandbox.io/static/js/sandbox.37038f94.js:1:186850
at https://codesandbox.io/static/js/sandbox.37038f94.js:1:185325
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 10
- Comments: 28 (7 by maintainers)
Commits related to this issue
- test: disable sync see vuejs/vue-test-utils#1130 — committed to vuetifyjs/vuetify by KaelWD 5 years ago
- fix(perf): avoid useless re-render on parent update (#8822) * fix(perf): avoid useless re-render on parent update fixes #6201 see vuejs/vue#7257 and vuejs/vue#10115 * test: disable sync see... — committed to vuetifyjs/vuetify by KaelWD 5 years ago
- Adds a workaround test for bug vuejs/vue-test-utils#1130 — committed to wearebraid/vue-formulate by justin-schroeder 5 years ago
- Add another test for Import view. Also upgrade @vue/test-utils to 1.0.0-beta.31 to get rid of its buggy 'sync' mode, which was causing "TypeError: Cannot read property '$scopedSlots' of undefined" fa... — committed to derat/mpticks by derat 4 years ago
- Downgrade to @vue/test-utils@1.0.0-beta.29 from .31. Downgrade to the latest version of vue-test-utils that includes sync mode since it seems to work around the v-treeview issue that forced me to dis... — committed to derat/mpticks by derat 4 years ago
- :bug: sync have to be off to avoid false positive test failed for more context https://github.com/vuejs/vue-test-utils/issues/1130 — committed to Torfab/opendrinks by Torfab 3 years ago
- Change print button for consistency and add tooltip for clarity (#1133) * Issue#1088 :sparkles: Changed share button in multiple buttons style * added mistaken removed part in a merge conflict ... — committed to alfg/opendrinks by Torfab 3 years ago
This is caused by the
syncmode change in beta.29.The workaround is to set
synctofalse:We may need to change the Vue Test Utils API and remove synchronous rendering entirely, which would require explicit calls to a
flushPromiseshelper function after each change that triggers a watcher.sync: falseworks. Caution that you need it on everyshallowMountormountin a single test file. I hadshallowMountwithout sync, and thenmountwith sync and still threw an error at the mount instead.Is there a clean way to ensure that every single shalllowMount calls gets passed
sync: falsewithout adding it manually?Thanks @eddyerburgh for the quick response.
I think this is quite a serious problem - it means many people who use watchers will suddenly start seeing tests fail with this error message - and confusingly it only seems to happen when running at least two tests - the first test always seems to work no problems. When our team tried to upgrade to beta.29 today hundreds of tests began failing (we’ve had to downgrade back to beta.28). Assuming no proper fix for this is possible we’ll need to add hundreds of flushPromises() calls throughout our tests.
Will the docs be updated soon with advice about this? And is no fix where the existing API is maintained possible?
@sobolevn When we had this issue we downgraded to 1.0.0-beta.28 where this issue doesn’t seem to occur. We’re waiting for the next release when sync mode will be removed and there will be some sort of migration guide - we plan to migrate our tests out of sync mode then.
@VladZen We have over 1500 tests using sync mode so I feel your pain, but I also respect that vue-test-utils is still in beta, so it’s not like a promise was ever made that the API would remain stable. Using the library to write a lot of tests was thus always a bit of a risk. I trust that @eddyerburgh and the core team are making decisions that will make Vue and vue-test-utils the best that they can be long-term, so I’m sure this will cause some short-term pain in exchange for a stable and quality library in the long term.
Seems to be a good news: i have about 400 tests, using sync mode and now it’s gonna be removed, nice work! You know, i appreciate all you’re doing for this project, but don’t you think it’s unfair to implement such a good feature and after users make tests for their projects you say like “It’s very buggy, it will be removed” instead of “Okay, we have bugs, but let’s try to fix them”?
I’m concerned there has not been much activity since this was reported 8 months ago now. Is testing not a major concern for VueJS? From what I can see there is a lot of focus is on getting 3.0 shipped but there still isn’t a solid reliable testing option for Vue yet.
I mean I understand this is still in beta, but I also don’t believe there are any alternatives. Avoriaz is no longer in active development as I understand it.
Considering this appears to be a regression from the previous beta release, is there any reason that this change wasn’t simply reverted? Or why the proposed fix of removing the
syncoption and defaulting tofalsebehaviour wasn’t implemented?@sobolevn
sync: falsesolved for me https://github.com/vuejs/vue-test-utils/issues/1130#issuecomment-460558486So, how can this be fixed?
Gives me an error:
I am using
"@vue/test-utils": "^1.0.0-beta.29",@danielelkington Yes it’s a very serious problem—bugs with sync mode is the reason we’re still in beta.
The only solution to avoid any edge cases and maintain a synchronous API in Vue Test Utils is to maintain a sync version of Vue and fix issues like this in the core repo, but I don’t think that’s realistic.
On top of that, Vue 3 probably won’t include synchronous updating either.
If we decide to remove sync mode, I’ll update the docs, add warnings, and make the migration as smooth as possible. But I understand it’s a huge change that will affect a lot of codebases, and I’m sorry for the extra work it will cause.
@VladZen we tried to fix them with multiple approaches, and all of them led to different bugs. This is the best approach for the long-term.
Thank you @danielelkington
@AtofStryker No need to create an extra issue, since the root cause is the same.
We’ve decided to remove sync mode, I’ll write-up the reasons why in the next few days, and we’ll remove sync mode from the next Vue Test Utils version.
Perhaps
flushPromisesshould be included as a method that can be imported from test utils?Finding that when running all my mocha tests it appears that any mount call in any test files can trigger this problem. So it looks like I have to go around and add
sync: falseandawait wrapper.vm.$nextTick()throughout all the tests.This seems wrong to me. Do I have this right? Is this being addressed in a future version of vue-test-utils?
Nope, I have just ignored it for now. Hopefully I can ignore this case. But, this is 100% required to be fixed.
@sobolevn have you resoved ? and how ?