Vue Test Utils: Watchers on object properties not triggered?

So I have this code that workes perfectly in Vue. But it behaves differently in tests.

watch: {
"first.subProp": function(val, old) {
this.updatedByWatcher = “IT WAS UPDATED”;
console.log("sub prop changing from: " + old + " to: " + val);
},
second: function(val, old) {
console.log("second changing from: " + old + " to: " + val);
},

In the tests the watcher on “first.subProp” is never triggered. Any input on this? Should this be reported as a bug ?

I don’t know the component or the test you wrote for it, so no - I don’t have any input.

Can you please provide an example, where you update first.subProp value?

I have clarified the issue and its not exactly what I thought. It happens outside of the tests too.
With this small code:

  watch: {
    "myProp.subProp": function(val, old) {
      console.log("subProp change: " + old + " => " + val);
    },
 methods: {
    update() {
      this.myProp.subProp = "change:" + new Date();   
    } 
 },

I call the update method from a button.

creating the component this way the watch works.

<my-component :myProp="myObj" />

creating the component this way the watch does not work.

<my-component :myProp="{ subProp: 'direct' }" />

it also does not work when setting props in router routes.

In tests:
This does not work.

const myObj = { subProp: 'sub prop' };
describe('watchers work', () => {
  const wrapper = shallowMount(Home, {
    propsData: {
      myProp: myObj,
    },
  });

after doing any of the following the watcher will start working

wrapper.setProps({ myProp:myObj});

or

wrapper.setProps({ myProp: { subProp: 'sub prop' } });

The code provided is currently not properly formatted for this forum. In it’s current state, it’s hardly readable and makes it hard for people to help you.

Please read the following guide about how to properly format code, and then edit your topic accordingly.

Thanks!

edited the example

Ok, now it’s clear.

This passes a plain javascript objetc to the child that is not reactive. Passing anything as props doesn’t make it reactive. Only objects that are part of a component’s data are made reactive.

Which also explains why this is not reactive:

None if this should be a problem unless you are mutating props within the child - which you should not do.

Thanks for the help.

I guess I do mutate it since I do myProp.subProp = ‘somehting’ in the child (but we dont do myProp=something). Right now we are crossing our fingers and hoping this wont come bite us in the ass, but the the size of the form we are doing the reactivity we need(the same prop can be displayed and modified in multiple components) and the time contraint we dont really see an other way. Still it would be good to get reactivity in the tests without having to call SetProps().

As for routing I feel this should work but there is no reactivity (ps. I dont need this right now but I see it being an issue for someone)

Vue.use(Router);
const myobj = { subProp: 'sub inject' };
const router = new Router({
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home,
      props: { myProp: myobj },
    },

Well, if you know what you’re doing and are aware that it’s something to have an eye on, you can make it work. We’ve all been there, I guess.

You could do this in your compontent:

export default {
  props: ['myProp'],
  data() {
    return {
      myLocalProp: this.myProp
    }
  }
}

That will make the object referenced through myProp reactive. You can even still continue to access it through myProp.

I’m going to test this out. Seems like a decent solution.

I ran into this issue when trying to test a deep watcher on a prop that is passed an object. If the parent component only updates a property in the object, whch triggers the deep watcher. It works in production, but Vue test utils fails because when calling wrapper.setProps(...) it spews a warning that I am passing the same object reference and it won’t trigger reactive updates:

[vue-test-utils]: wrapper.setProps() called with the same object of the existing filter property. You must call wrapper.setProps() with a new object to trigger reactivity

In fact, it works in production (because of the deep watcher), but it can’t be tested in vue-test-utils because of the above error.