Skip to content

Commit befd9d0

Browse files
authored
Add mocks guide (#28)
* Add mocks guide * Lint
1 parent 3dd4daa commit befd9d0

File tree

6 files changed

+168
-5
lines changed

6 files changed

+168
-5
lines changed

demo-app/jest.config.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ module.exports = {
1818
],
1919
testMatch: [
2020
'<rootDir>/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
21-
]
21+
],
22+
setupFiles: ["<rootDir>/jest.init.js"]
2223
}

demo-app/jest.init.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import VueTestUtils from "@vue/test-utils"
2+
import translations from "./src/translations.js"
3+
4+
const locale = "en"
5+
6+
VueTestUtils.config.mocks["$t"] = (msg) => translations[locale][msg]

demo-app/src/components/Bilingual.vue

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<template>
2+
<div class="hello">
3+
{{ $t("helloWorld") }}
4+
</div>
5+
</template>
6+
7+
<script>
8+
export default {
9+
name: "Bilingual"
10+
}
11+
</script>

demo-app/src/translations.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default {
2+
"en": {
3+
helloWorld: "Hello world!"
4+
},
5+
"ja": {
6+
helloWorld: "こんにちは、世界!"
7+
}
8+
}

demo-app/tests/unit/Bilingual.spec.js

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { shallowMount } from "@vue/test-utils"
2+
import Bilingual from "@/components/Bilingual.vue"
3+
4+
describe("Bilingual", () => {
5+
it("renders successfully", () => {
6+
const wrapper = shallowMount(Bilingual)
7+
8+
expect(wrapper.find(".hello").text()).not.toBe("")
9+
})
10+
})

src/mocking-global-objects.md

+131-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,134 @@
11
### Mocking global objects
22

3-
ideas:
3+
`vue-test-utils` provides a simple way to global objects attached to `Vue.prototype`, both on test by test basis and to set a default mock for all tests.
44

5-
- $store
6-
- $router
7-
- $t (vue i18n)
5+
The test used in the following example can be found [here](https://github.com/lmiller1990/vue-testing-handbook/blob/master/demo-app/tests/unit/Bilingual.spec.js).
6+
7+
## The mocks mounting option
8+
9+
The [mocks mounting optino](https://vue-test-utils.vuejs.org/api/options.html#mocks) is one way to set the value of any properties attached to `Vue.prototype`. This commonlly includes:
10+
11+
- `$store`, for Vuex
12+
- `$router`, for Vue Router
13+
- `$t`, for vue-i18n
14+
15+
any many others.
16+
17+
## Example with vue-i18n
18+
19+
Use with Vuex and Vue Router are discussed in the respective sections. Let's see an example with [vue-i18n](https://github.com/kazupon/vue-i18n). While it would be possible to use `createLocalVue` and install `vue-i18n` for each test, that would quickly get cumbersome and introduce boilerplate. First, a `<Bilingual>` component that uses `vue-i18n`:
20+
21+
```html
22+
<template>
23+
<div class="hello">
24+
{{ $t("helloWorld") }}
25+
</div>
26+
</template>
27+
28+
<script>
29+
export default {
30+
name: "Bilingual"
31+
}
32+
</script>
33+
```
34+
35+
The way `vue-i18n` works is you declare you translation in another file, then reference them with `$t`. For the purpose of this test it doesn't really matter, but for this component the translation file would be like this:
36+
37+
```js
38+
export default {
39+
"en": {
40+
helloWorld: "Hello world!"
41+
},
42+
"ja": {
43+
helloWorld: "こんにちは、世界!"
44+
}
45+
}
46+
```
47+
48+
Basic on the locale, the correct translation is rendered. Let's try and render the component in a test:
49+
50+
```js
51+
import { shallowMount } from "@vue/test-utils"
52+
import Bilingual from "@/components/Bilingual.vue"
53+
54+
describe("Bilingual", () => {
55+
it("renders successfully", () => {
56+
const wrapper = shallowMount(Bilingual)
57+
})
58+
})
59+
```
60+
61+
Running this test with `yarn test:unit` throw huge stack trace. If you look through, you can see:
62+
63+
```
64+
[Vue warn]: Error in config.errorHandler: "TypeError: _vm.$t is not a function"
65+
```
66+
67+
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:
68+
69+
```js
70+
import { shallowMount } from "@vue/test-utils"
71+
import Bilingual from "@/components/Bilingual.vue"
72+
73+
describe("Bilingual", () => {
74+
it("renders successfully", () => {
75+
const wrapper = shallowMount(Bilingual, {
76+
mocks: {
77+
$t: (msg) => msg
78+
}
79+
})
80+
})
81+
})
82+
```
83+
84+
Now the test passes! There are lots of uses for the `mocks` option. Most frequently I find myself mocking the three packages mentioned above.
85+
86+
## Settings default mocks using config
87+
88+
Soemtimes you want to have a default value for the mock, so you don't have to type it for every test. You can do this using the [config](https://vue-test-utils.vuejs.org/api/#vue-test-utils-config-options) provided by `vue-test-utils`. Let's expand the `vue-i18n` example. You can set default mocks anywhere by doing the following:
89+
90+
```js
91+
import VueTestUtils from "@vue/test-utils"
92+
93+
VueTestUtils.config.mocks["mock"] = "Default Mock Value"
94+
```
95+
96+
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 translations object declared above, and use it in the mock implementation.
97+
98+
```js
99+
import VueTestUtils from "@vue/test-utils"
100+
import translations from "./src/translations.js"
101+
102+
const locale = "en"
103+
104+
VueTestUtils.config.mocks["$t"] = (msg) => translations[locale][msg]
105+
```
106+
107+
Now a real translation will be rendered, even using the mocked `$t` function. Run the test again, this time using `console.log` on `wrapper.html()` and removing the `mocks` mounting option:
108+
109+
```js
110+
describe("Bilingual", () => {
111+
it("renders successfully", () => {
112+
const wrapper = shallowMount(Bilingual)
113+
114+
console.log(wrapper.html())
115+
})
116+
})
117+
```
118+
119+
The test passes, and the following markup is rendered:
120+
121+
```
122+
<div class="hello">
123+
Hello world!
124+
</div>
125+
```
126+
127+
You can read about using `mocks` to test Vuex [here](https://lmiller1990.github.io/vue-testing-handbook/vuex-in-components.html#using-a-mock-store). The technique is the same.
128+
129+
## Conclusion
130+
131+
This guide discussed:
132+
133+
- using `mocks` to mock a global object on a test by test basis
134+
- using `config.mocks` to set a default mock

0 commit comments

Comments
 (0)