Mocking Global Objects
Bài đăng này đã không được cập nhật trong 4 năm
Mocking global objects
vue-test-utils
provides a simple way to mock global objects attached to Vue.prototype
, both on test by test basis and to set a default mock for all tests.
The test used in the following example can be found here.
The mocks mounting option
The mocks mounting option is one way to set the value of any properties attached to Vue.prototype
. This commonly includes:
$store
, for Vuex$router
, for Vue Router$t
, for vue-i18n
and many others.
Example with vue-i18n
Use with Vuex and Vue Router are discussed in the respective sections, here and here. Let's see an example with vue-i18n. While it would be possible to use createLocalVue
and install vue-i18n
for each test, that would quickly get cumbersome and introduce a lot of boilerplate. First, a <Bilingual>
component that uses vue-i18n
:
<template>
<div class="hello">
{{ $t("helloWorld") }}
</div>
</template>
<script>
export default {
name: "Bilingual"
}
</script>
The way vue-i18n
works is you declare your translation in another file, then reference them with $t
. For the purpose of this test it doesn't really matter what the translation file looks like, but for this component it could look like this:
export default {
"en": {
helloWorld: "Hello world!"
},
"ja": {
helloWorld: "こんにちは、世界!"
}
}
Based on the locale, the correct translation is rendered. Let's try and render the component in a test, without any mocking.
import { shallowMount } from "@vue/test-utils"
import Bilingual from "@/components/Bilingual.vue"
describe("Bilingual", () => {
it("renders successfully", () => {
const wrapper = shallowMount(Bilingual)
})
})
Running this test with yarn test:unit
throws a huge stack trace. If you look through the output carefully, you can see:
[Vue warn]: Error in config.errorHandler: "TypeError: _vm.$t is not a function"
This is because we did not install vue-i18n
, so the global $t
method does not exist. Let's mock it using the mocks
mounting option:
import { shallowMount } from "@vue/test-utils"
import Bilingual from "@/components/Bilingual.vue"
describe("Bilingual", () => {
it("renders successfully", () => {
const wrapper = shallowMount(Bilingual, {
mocks: {
$t: (msg) => msg
}
})
})
})
Now the test passes! There are lots of uses for the mocks
option. Most frequently I find myself mocking the global objects provided by the three packages mentioned above.
Settings default mocks using config
Sometimes you want to have a default value for the mock, so you don't create it on a test by test basis. You can do this using the config API provided by vue-test-utils
. Let's expand the vue-i18n
example. You can set default mocks anywhere by doing the following:
import VueTestUtils from "@vue/test-utils"
VueTestUtils.config.mocks["mock"] = "Default Mock Value"
The demo project for this guide is using Jest, so I will declare the default mock in jest.init.js
, which is loaded before the tests are run automatically. I will also import the example translations object from earlier, and use it in the mock implementation.
import VueTestUtils from "@vue/test-utils"
import translations from "./src/translations.js"
const locale = "en"
VueTestUtils.config.mocks["$t"] = (msg) => translations[locale][msg]
Now a real translation will be rendered, despite using a mocked $t
function. Run the test again, this time using console.log
on wrapper.html()
and removing the mocks
mounting option:
describe("Bilingual", () => {
it("renders successfully", () => {
const wrapper = shallowMount(Bilingual)
console.log(wrapper.html())
})
})
The test passes, and the following markup is rendered:
<div class="hello">
Hello world!
</div>
You can read about using mocks
to test Vuex here. The technique is the same.
Conclusion
This guide discussed:
- using
mocks
to mock a global object on a test by test basis - using
config.mocks
to set a default mock
All rights reserved