0

I'm running into this problem that I can't figure out and looking around can't seem to find anyone asking the same questions...

So, here's my working code:

main.js:

import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

window.Vue = Vue

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  methods: {
    greetings: function () {
      return 'good morning'
    }
  },
  render: h => h(App)
})

router/index.js:

import Vue from 'vue'
import Router from 'vue-router'
import Configurations from '@/components/Configurations'

Vue.use(Router)
export default new Router({
  routes: [
    {
      path: '/configs',
      name: 'Configurations',
      component: Configurations
    }
  ]
})

App.vue:

<template>
    <router-view></router-view>
</template>
<script>
export default {
  name: "App"
};
</script>

Configurations.vue:

<template>
<span>{{greetings}}</span>
</template>

<script>
export default {
  name: "configurations",
  data() {
    return {
      greetings: ""
    };
  },
  created: function() {
    this.greetings = this.$root.greetings();
  }
};
</script>

When I run this, it displays a page with the words "good morning" on it. Nothing else. As you can see, the greetings message is defined on the main.js file, because in this scenario I'd like a message to be displayed on all components, ie: a copyright note. So I set the message on the common ancestor to all my components.

From my understanding, to call a method from a "parent" component, you use this.$root.yourMethod() to call it.

Now, the problem is, that if I wish to run a unit-test for the Configurations component, I get an error which leads me to believe that the way I have things set up, I cannot test the this.$root.greetings() because something tells me the testing environment is unaware of the parent-child relationship between the components.

So, when I run:

cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run

Having the following test file set up:

Configurations.spec.js:

import Configurations from '@/components/Configurations'
import Vue from 'vue'

describe('Configurations.vue', () => {
  it('should display a greetings message', () => {
    const Constructor = Vue.extend(Configurations)
    const ConfigurationsComponent = new Constructor().$mount()
    expect(ConfigurationsComponent.greetings).to.not.be.undefined;
  })
})

I get:

> 16 10 2018 09:28:35.184:INFO [karma]: Karma v1.7.1 server started at
> http://0.0.0.0:3000/ 16 10 2018 09:28:35.191:INFO [launcher]:
> Launching browser PhantomJS with unlimited concurrency 16 10 2018
> 09:28:35.204:INFO [launcher]: Starting browser PhantomJS 16 10 2018
> 09:28:40.977:INFO [PhantomJS 2.1.1 (Windows 8.0.0)]: Connected on
> socket WnDl-mQqmNZQOfyzAAAA with id 10303480 ERROR LOG: '[Vue warn]:
> Error in created hook: "TypeError:  is not a constructor (evaluating
> 'this.$root.greetings()')"

(followed by more lines that stack-overflow won't let me post because they contain too many links)

Now, if I were to change

this.greetings = this.$root.greetings()

To:

this.greetings = "good morning";

In the Configurations.vue file, and then run the test command again, I'd get:

> 16 10 2018 09:48:43.174:INFO [karma]: Karma v1.7.1 server started at
> http://0.0.0.0:3000/ 16 10 2018 09:48:43.180:INFO [launcher]:
> Launching browser PhantomJS with unlimited concurrency 16 10 2018
> 09:48:43.555:INFO [launcher]: Starting browser PhantomJS 16 10 2018
> 09:48:49.228:INFO [PhantomJS 2.1.1 (Windows 8.0.0)]: Connected on
> socket o8BQ24wv1QX26SAZAAAA with id 53463701
> 
>   Configurations.vue
>     √ should display a greetings message
>
> PhantomJS 2.1.1 (Windows 8.0.0): Executed 1 of 1 SUCCESS (0.024 secs / 0.017 secs)
>
> TOTAL: 1 SUCCESS

(followed by some other information that's irrelevant)

So, how can I run a unit-test for a component that contains calls to this.$root methods?

Any help is appreciated.

  • Lucas

1 Answer 1

0

You should (nearly) never call something from a parent component.

You have 2 possibilities: 1. Use a vuex store and save your data there. Then every component in need can access it by a store getter. 2. Provide the needed data as props to your child components.

Because the props thing gets very quick messy a suggest to use the store.

If you do so your tests will become much clearer and easier to read.

Simple test with a mocked store (just to show how things could be):

describe('MyComponent', () => {
  let wrapper

  beforeEach(() => {
    const localVue = createLocalVue()
    wrapper = mount(MyComponent, {
      localVue,
      mocks: {
        $store: {
          getters: {
            'copyright': 'MyCopyright text'
          }
        }
      }
    })
  })

  it('should display copyright text', () => {
    expect(wrapper.text('MyCopyright text')).toBe(true) 
  })
})
Sign up to request clarification or add additional context in comments.

5 Comments

I understand. But for very simple things is there no other option? I'll give vuex a try but seems a bit overkill for something simple. Thanks!
I tried your suggestion and it works fine up to the point where I decide to test it: Now the same problem occurs but now its complaining about this.$store rather than this.$root My testing environment is just not aware of main.js apparently...
Your environment is not supposed to know your main.js in unit tests. I updated my answer with a simple test mocking the store. It is using jest as test runner.
Hey Thomas, thanks again. Actually, after you suggested vuex I went searching for this topic again and found my answers. This helped me in the end: stackoverflow.com/questions/42542697/… The store is now available within my spec file which is the key I was missing and its very similar to your suggestion. While I didn't test your code, I'm still accepting your answer for leading me in the right tracks. Seems I still have quite a bit to learn with unit-testing and vue. THanks!
The most difficult step is always the first one! ;)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.