|
1 | 1 | ### Mocking global objects
|
2 | 2 |
|
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. |
4 | 4 |
|
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