How to test Vue "Services" mounted to root, accessed via Vue.prototype

561 views Asked by At

First, I'd like to explain that I have a Vue component repository that is responsible for displaying data retrieved from an http service. Rather than the component itself managing the same data retrieval per instance and spamming the client with network requests, I've managed to find a solution which allows another component to be mounted to the root directly (which I've dubbed as a "Service" due to its similarity to Angular) to manage the data those components need instead. This works great and other components can access it via Vue.prototype (via this.$TestService.value). It has some caveats but for the most part it accomplishes exactly what I needed. This may be uncommon, but those that use Vuex are using a similar methodology and I don't want to use the store paradigm.

I've made a very simple Vue JsFiddle to show this in action...

https://jsfiddle.net/spronkets/8v31tcfd/18

Now, to the point... I'm using @testing-library/vue, @vue/test-utils, and Jest to test the components and get test coverage and now I get errors anytime I run the tests due to the service not existing on the Vue.prototype during the test execution. I don't want to mock out the functionality of the "Service" layer, so does anyone have a solution to test these root-mounted components? I've tried manually exporting the services (unmounted and mounted) and including them in the mock section as well as importing the files directly into the test files but the "Service" is always undefined when the component is trying to retrieve the value and ONLY during test execution...

I've also created a simple repository modelled after the Vue component repository I am working with below...

https://github.com/kcrossman/VueServiceExample

To get started, clone the repo and follow the README.md included in the repo. Thanks!

3

There are 3 answers

0
Kody On BEST ANSWER

Update: For those that might be referring to this in the future, Vue Plugins might be a better solution for this kind of functionality.


I stumbled along this issue in GitHub and that led me to the fix I made below:

https://github.com/testing-library/vue-testing-library/issues/113

Specifically, this comment by user nikravi:

ok, I found the fix. The trick was to add

import Vue from "vue";
import Vuetify from "vuetify";
Vue.use(Vuetify);

and then the render() works without warnings.

After I manually imported Vue and set Vue.prototype.$TestService = TestService directly in the unit test, it got passed that error. Personally, I think this is pretty silly, but it worked.

After this worked, I also found that you can access the Vue instance directly within the render callback (from @testing-library/vue), so I finished on this code instead of importing Vue:

render(TestComponent, {}, vue => {
    vue.prototype.$TestService = TestService;
});

I've included all the commits to solve my issue in the repo I posted previously:

https://github.com/kcrossman/VueServiceExample

Some of the tests were malformed but once I made those changes, the tests started to work and I updated some other files to be a bit nicer for people to refer to.

1
CodingLikeDavid On

You need to create and prepare your custom Vue instance in your tests in order to use any custom functionalities in your unit tests (like stores, routers, and anything else). (You can use your real modules with the custom instance, don't have to mock anything.)

In your case you should create a new Vue instance with "createLocalVue" function from '@vue/test-utils' and apply your custom prototype functionalities on that. After that you can write proper test cases accessing that custom features as well.

1
Eder Díaz On

I would go against using the real service if it is asyncronous, but if you just want to register it to be available you can follow the mock instructions but instead of mocking with an object just import the real service. Although after seeing your TestService implementation you will need to separate the real service from the service registration and export it to be able to register it in local vue.