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
:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>template</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>hello<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> {{ $t("helloWorld") }} <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>template</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script language-javascript"> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> name<span class="token punctuation">:</span> <span class="token string">"Bilingual"</span> <span class="token punctuation">}</span> </span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> |
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:
1 2 3 4 5 6 7 8 9 | <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token string">"en"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span> helloWorld<span class="token punctuation">:</span> <span class="token string">"Hello world!"</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token string">"ja"</span><span class="token punctuation">:</span> <span class="token punctuation">{</span> helloWorld<span class="token punctuation">:</span> <span class="token string">"こんにちは、世界!"</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> |
Based on the locale, the correct translation is rendered. Let’s try and render the component in a test, without any mocking.
1 2 3 4 5 6 7 8 9 | <span class="token keyword">import</span> <span class="token punctuation">{</span> shallowMount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@vue/test-utils"</span> <span class="token keyword">import</span> Bilingual <span class="token keyword">from</span> <span class="token string">"@/components/Bilingual.vue"</span> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"Bilingual"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"renders successfully"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Bilingual<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
Running this test with yarn test:unit
throws a huge stack trace. If you look through the output carefully, you can see:
1 2 | [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:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">import</span> <span class="token punctuation">{</span> shallowMount <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"@vue/test-utils"</span> <span class="token keyword">import</span> Bilingual <span class="token keyword">from</span> <span class="token string">"@/components/Bilingual.vue"</span> <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"Bilingual"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"renders successfully"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Bilingual<span class="token punctuation">,</span> <span class="token punctuation">{</span> mocks<span class="token punctuation">:</span> <span class="token punctuation">{</span> $t<span class="token punctuation">:</span> <span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token operator">=></span> msg <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
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:
1 2 3 4 | <span class="token keyword">import</span> VueTestUtils <span class="token keyword">from</span> <span class="token string">"@vue/test-utils"</span> VueTestUtils<span class="token punctuation">.</span>config<span class="token punctuation">.</span>mocks<span class="token punctuation">[</span><span class="token string">"mock"</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token string">"Default Mock Value"</span> |
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.
1 2 3 4 5 6 7 | <span class="token keyword">import</span> VueTestUtils <span class="token keyword">from</span> <span class="token string">"@vue/test-utils"</span> <span class="token keyword">import</span> translations <span class="token keyword">from</span> <span class="token string">"./src/translations.js"</span> <span class="token keyword">const</span> locale <span class="token operator">=</span> <span class="token string">"en"</span> VueTestUtils<span class="token punctuation">.</span>config<span class="token punctuation">.</span>mocks<span class="token punctuation">[</span><span class="token string">"$t"</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">(</span>msg<span class="token punctuation">)</span> <span class="token operator">=></span> translations<span class="token punctuation">[</span>locale<span class="token punctuation">]</span><span class="token punctuation">[</span>msg<span class="token punctuation">]</span> |
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:
1 2 3 4 5 6 7 8 | <span class="token function">describe</span><span class="token punctuation">(</span><span class="token string">"Bilingual"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">it</span><span class="token punctuation">(</span><span class="token string">"renders successfully"</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">const</span> wrapper <span class="token operator">=</span> <span class="token function">shallowMount</span><span class="token punctuation">(</span>Bilingual<span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>wrapper<span class="token punctuation">.</span><span class="token function">html</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> |
The test passes, and the following markup is rendered:
1 2 3 4 | <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