vue-testing v0.0.3
Vue Testing
Let's make Vue Testing And Mocking Become Easier And Much Fun.
Installation
npm install vue-testing inject-loader --save-dev
Motivation
If you're testing without js-testing
you'll realize that you have a long boilerplate code to mock up your component. Let me show you how it looks. It's taken from https://vue-loader.vuejs.org/en/workflow/testing-with-mocks.html
<!-- example.vue -->
<template>
<div class="msg">{{ msg }}</div>
</template>
<script>
// this dependency needs to be mocked
import SomeService from '../service'
export default {
data () {
return {
msg: SomeService.msg
}
}
}
</script>
// example.spec.js
// Import the component and make the injection preparation
const ExampleInjector = require('!!vue?inject!./example.vue')
// Inject and Mock External Resource
const ExampleWithMocks = ExampleInjector({
// mock it
'../service': {
msg: 'Hello from a mocked service!'
}
})
it('should render', () => {
// Render the component manually
const vm = new Vue({
template: '<div><test></test></div>',
components: {
'test': ExampleWithMocks
}
}).$mount()
expect(vm.$el.querySelector('.msg').textContent).toBe('Hello from a mocked service!')
})
Magic Vue Properties
Probably, you'll not get frustated with that code. Cause it's simple. until your component uses vue properties that magically injected by some plugin. For example js-router
injects this.$router
and this.$route
Or jsx
injects this.$store
. It will throw error variable undefined since we not render the whole app. So, How do you handle it without touching your component code? It needs some effort.
<!-- example.vue -->
<template>
<div class="msg">{{ msg }}</div>
</template>
<script>
// this dependency needs to be mocked
import SomeService from '../service'
export default {
data () {
return {
msg: SomeService.msg
}
},
created() {
// Use Magic Properties
if(this.$route.params === someCondition) this.$router.push('/login')
}
}
</script>
// example.spec.js
// Import the component and make the injection preparation
const ExampleInjector = require('!!vue?inject!./example.vue')
// Inject and Mock External Resource
const ExampleWithMocks = ExampleInjector({
// mock it
'../service': {
msg: 'Hello from a mocked service!'
}
})
// You can inject magic properties this way
ExampleWithMocks.beforeCreate = function () {
this.$route = { params: {} }
this.$router = { push: () => {} }
}
it('should render', () => {
// Render the component manually
const vm = new Vue({
template: '<div><test></test></div>',
components: {
'test': ExampleWithMocks
}
}).$mount()
expect(vm.$el.querySelector('.msg').textContent).toBe('Hello from a mocked service!')
})
Vuex Helper Function
Oke, let's say that you always pass the router params via props since js-router@2.2.x
. But you'll still get the problem when you're using vuex helper function like mapActions
, mapStates
, mapGetters
. Sure, you need write more.
<!-- example.vue -->
<template>
<div class="msg">{{ msg }}</div>
</template>
<script>
// this dependency needs to be mocked
import SomeService from '../service'
// Vuex Helper Function
import { mapActions } from 'vuex'
export default {
data () {
return {
msg: SomeService.msg
}
},
methods: {
// Use It!
...mapActions({
test: 'SOME_ACTION'
})
},
created() {
// Use Magic Properties
if(this.$route.params === someCondition) this.$router.push('/login')
// Bind It!
this.test()
}
}
</script>
// example.spec.js
// Import the component and make the injection preparation
const ExampleInjector = require('!!vue?inject!./example.vue')
// Inject and Mock External Resource
const ExampleWithMocks = ExampleInjector({
// mock it
'../service': {
msg: 'Hello from a mocked service!'
},
'vuex': {
mapActions: () => {}
}
})
// Mock The Method To Put Your Spy
ExampleWithMocks.methods = {
...ExampleWithMocks.methods,
test: function() {
return spy
}
}
// You can inject magic properties this way
ExampleWithMocks.beforeCreate = function () {
this.$route = { params: {} }
this.$router = { push: () => {} }
}
it('should render', () => {
// Render the component manually
const vm = new Vue({
template: '<div><test></test></div>',
components: {
'test': ExampleWithMocks
}
}).$mount()
expect(vm.$el.querySelector('.msg').textContent).toBe('Hello from a mocked service!')
})
Let's Make It Simple!
Let's make it simple with js-testing
. You'll just need little effort to mock your component constructor. Take a peek.
// example.spec.js
// Import Vue Testing Helpers
import { mockComponent, mount } from 'vue-testing';
// Import the component and make the injection preparation
const ExampleInjector = require('!!vue?inject!./example.vue')
// Mock It!
let Component = mockComponent(ExampleInjector, {
// You can inject component properties via propsData
propsData: {
msg: 'hai'
},
// Even You can inject the local state without breaking your beforeCreate function
// It will save you from mapStates and mapGetters vuex function error in testing
states: {
localState: 'hello',
$route: {
params: {}
},
$router: {
push: () => {}
}
},
// You can inject the actions to be your local methods!
// It will save you from mapActions vuex function error in testing
actions: {
test: () => spy
},
// Default integrated with vuex-saga
// It will save you from mapSagas vuex-saga function error in testing
sagas: {
test: () => {}
},
// Mock the external module
// You can just put all in one scope
'../service': {
msg: 'Hello from a mocked service!'
},
'jquery': () => {}
'external-module': {}
})
describe('ExampleComponent', function () {
let vm;
beforeEach(function () {
// Mount it!
// Mount it in every test scope. So you'll get fresh component
vm = mount(Component)
})
// Focus On Your Test!
it('Some Test', function () {
const actual = vm.$el.querySelector('.msg').textContent
const expected = 'Hello from a mocked service!'
expect(actual).toBe(expected)
})
})
Have You Tested Your Components? You Should Be~
Thank You for Making this useful~
Let's talk about some projects with me
Just Contact Me At:
- Email: bosnaufalemail@gmail.com
- Skype Id: bosnaufal254
- twitter: @BosNaufal
License
MIT Copyright (c) Naufal Rabbani