In a real world application, our components most likely have external dependencies. When writing unit tests for components, it would be ideal if we can mock these external dependencies so that our tests only rely the behavior of the component being tested.
vue-loader
provides a feature that allows you to inject arbitrary dependencies to a *.vue
component, using inject-loader. The general idea is that instead of directly importing the component module, we use inject-loader
to create a "module factory" function for that module. When this function gets called with an object of mocks, it returns an instance of the module with the mocks injected.
Note: You must disable
esModule
option in inject mode, or you will get an error.
Suppose we have a component like this:
<!-- example.vue -->
<template>
<div class="msg">{{ msg }}</div>
</template>
<script>
// this dependency needs to be mocked
const SomeService = require('../service')
module.exports = {
data () {
return {
msg: SomeService.msg
}
}
}
</script>
Here's how to import it with mocks:
npm install inject-loader --save-dev
// example.spec.js
const ExampleInjector = require('!!vue-loader?inject!./example.vue')
Notice that crazy require string - we are using some inline webpack loader requests here. A quick explanation:
!!
at the start means "disable all loaders from the global config";vue-loader?inject!
means "usevue-loader
, and pass in the?inject
query". This tellsvue-loader
to compile the component in dependency-injection mode.
The returned ExampleInjector
is a factory function that can be called to create instances of the example.vue
module:
const ExampleWithMocks = ExampleInjector({
// mock it
'../service': {
msg: 'Hello from a mocked service!'
}
})
Finally, we can test the component like usual:
it('should render', () => {
const vm = new Vue({
template: '<div><test></test></div>',
components: {
'test': ExampleWithMocks
}
}).$mount()
expect(vm.$el.querySelector('.msg').textContent).to.equal('Hello from a mocked service!')
})