Activate Event
One of the most common things Vue components will do is to listen for user input. vue-test-utils
and Jest make it easy to check the input. Let’s look at how to use trigger
and Jest mocks to verify component components are working correctly.
The source code for the test described on this page can be found here .
Create component
We will create a simple component <FormSubmitter>
, which contains a <input>
and a <button>
. When the button is clicked, something happens. The first example will only display a success message, then we’ll move on to a more interesting example like submitting the form to an external endpoint.
Create a <FormSubmitter>
and enter the template template:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <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 punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> form</span> <span class="token attr-name">@submit.prevent</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> handleSubmit <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> input</span> <span class="token attr-name">v-model</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> username <span class="token punctuation">"</span></span> <span class="token attr-name">data-username</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> input</span> <span class="token attr-name">type</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> submit <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> form</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> message <span class="token punctuation">"</span></span> <span class="token attr-name">v-if</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> submitted <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> Thank you for your submission, {{ username }}. <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> 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> |
When the user submits the form, we will receive a message thanking them for submitting. We want to submit the form asynchronously, so we are using the @submit.prevent
mechanism to prevent the default action, which is to refresh the page when the form is submitted.
Now add the submit form logic:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <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">"FormSubmitter"</span> <span class="token punctuation">,</span> <span class="token function">data</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> username <span class="token punctuation">:</span> <span class="token string">''</span> <span class="token punctuation">,</span> submitted <span class="token punctuation">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> methods <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">handleSubmit</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> submitted <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">}</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> |
Quite simply, we only set the submitted
true
when the form was submitted, thus displaying the success message in the <div>
tag.
Write a test
The test file is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <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> FormSubmitter <span class="token keyword">from</span> <span class="token string">"@/components/FormSubmitter.vue"</span> <span class="token function">describe</span> <span class="token punctuation">(</span> <span class="token string">"FormSubmitter"</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">"reveals a notification when submitted"</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> FormSubmitter <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"[data-username]"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"alice"</span> <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"form"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">trigger</span> <span class="token punctuation">(</span> <span class="token string">"submit.prevent"</span> <span class="token punctuation">)</span> <span class="token function">expect</span> <span class="token punctuation">(</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">".message"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">text</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toBe</span> <span class="token punctuation">(</span> <span class="token string">"Thank you for your submission, alice."</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> |
We use the shallowMount
component, naming the user using the trigger
and vue-test-utils
methods provide simulation of the user’s input. trigger
works on custom events, and events using a modifier, as submit.prevent
, keydown.enter
…
This test also follows the 3 steps of unit testing:
- sort (test setup. In our case, we render the component)
- action (perform actions on the system)
- assert (make sure the actual results match your expectations)
We separate each step with a new line because it makes the test more readable.
Run the test with yarn test:unit
. Of course it will pass.
The trigger is very simple – use find
to get the desired element to simulate some input and call the trigger
with the name of the event and any modification tools.
Practical examples
Forms are usually submitted to certain endpoints. Let’s see if we can test this component with a different implementation than handleSubmit
. A common practice is that the alias of the HTTP library is Vue.prototype.$http
. This allows us to make an Ajax request by simply calling this.$http.get(...)
. Find out more here .
Typically the HTTP library is axios, a common current HTTP client. In this case, we use handSubmit
which might look like this:
1 2 3 4 5 6 7 8 9 10 | <span class="token function">handleSubmitAsync</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> $http <span class="token punctuation">.</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token string">"/api/v1/register"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> username <span class="token punctuation">:</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> username <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</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 comment">// show success message, etc</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</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 comment">// handle error</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> |
In this case, a mock this.$http
technique this.$http
will create a desired test field. You can read about mocks
described further [here]] ( https://vue-test-utils.vuejs.org/api/options.html#mocks ). Let’s mock an http.get
method as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token string">''</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token string">''</span> <span class="token keyword">const</span> mockHttp <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">:</span> <span class="token punctuation">(</span> _url <span class="token punctuation">,</span> _data <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> resolve <span class="token punctuation">,</span> reject <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> url <span class="token operator">=</span> _url data <span class="token operator">=</span> _data <span class="token function">resolve</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> |
There are a few interesting things going on here:
- We create a variable
url
anddata
to saveurl
anddata
transfer up to$http.get
. This will be helpful to verify that the request is going to the correct endpoint with the correct payload. - After assigning the
url
anddata
arguments, we resolve the Promise. to successfully API feedback.
Before viewing the test file, here’s the new handleSubmitAsync
function:
1 2 3 4 5 6 7 8 9 10 11 12 | methods <span class="token punctuation">:</span> <span class="token punctuation">{</span> <span class="token function">handleSubmitAsync</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> $http <span class="token punctuation">.</span> <span class="token keyword">get</span> <span class="token punctuation">(</span> <span class="token string">"/api/v1/register"</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> username <span class="token punctuation">:</span> <span class="token keyword">this</span> <span class="token punctuation">.</span> username <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">then</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">this</span> <span class="token punctuation">.</span> submitted <span class="token operator">=</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> e <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">throw</span> <span class="token function">Error</span> <span class="token punctuation">(</span> <span class="token string">"Something went wrong"</span> <span class="token punctuation">,</span> e <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> |
In addition, we also update the <template>
to use the new handleSubmitAsync
function:
1 2 3 4 5 6 7 8 9 10 11 | <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 punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> form</span> <span class="token attr-name">@submit.prevent</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> handleSubmitAsync <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> input</span> <span class="token attr-name">v-model</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> username <span class="token punctuation">"</span></span> <span class="token attr-name">data-username</span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span> input</span> <span class="token attr-name">type</span> <span class="token attr-value"><span class="token punctuation">=</span> <span class="token punctuation">"</span> submit <span class="token punctuation">"</span></span> <span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span> form</span> <span class="token punctuation">></span></span> <span class="token comment"><!-- ... --></span> <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> |
Now, we just have to test.
Mocking an ajax call
First, include mock this.$http
at the top, before the describe
block:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <span class="token keyword">let</span> url <span class="token operator">=</span> <span class="token string">''</span> <span class="token keyword">let</span> data <span class="token operator">=</span> <span class="token string">''</span> <span class="token keyword">const</span> mockHttp <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token keyword">get</span> <span class="token punctuation">:</span> <span class="token punctuation">(</span> _url <span class="token punctuation">,</span> _data <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> resolve <span class="token punctuation">,</span> reject <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> url <span class="token operator">=</span> _url data <span class="token operator">=</span> _data <span class="token function">resolve</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, to add a test case, use $http
with mocks
as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token function">it</span> <span class="token punctuation">(</span> <span class="token string">"reveals a notification when submitted"</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> FormSubmitter <span class="token punctuation">,</span> <span class="token punctuation">{</span> mocks <span class="token punctuation">:</span> <span class="token punctuation">{</span> $http <span class="token punctuation">:</span> mockHttp <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"[data-username]"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"alice"</span> <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"form"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">trigger</span> <span class="token punctuation">(</span> <span class="token string">"submit.prevent"</span> <span class="token punctuation">)</span> <span class="token function">expect</span> <span class="token punctuation">(</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">".message"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">text</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toBe</span> <span class="token punctuation">(</span> <span class="token string">"Thank you for your submission, alice."</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Instead of using any http library using Vue.prototype.$http
, the mock implementation will be used instead. This we can control the environment of the test and get consistent results.
Running yarn test:unit
will produce a fail test result:
1 2 3 4 5 | FAIL tests/unit/FormSubmitter.spec.js ● FormSubmitter › reveals a notification when submitted [vue-test-utils]: find did not return .message, cannot call text() on empty Wrapper |
The small problem is that our test is done before the promise before returning resolves mockHttp
. We can solve it using async
like this:
1 2 3 4 | <span class="token function">it</span> <span class="token punctuation">(</span> <span class="token string">"reveals a notification when submitted"</span> <span class="token punctuation">,</span> <span class="token keyword">async</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token comment">// ...</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
However, our test still ends before the promise resolves. One way to solve this problem is to use flush-promises , a simple Node.js module that will immediately resolve all pending promises . Install yarn add flush-promises
, update the test changes as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">import</span> flushPromises <span class="token keyword">from</span> <span class="token string">"flush-promises"</span> <span class="token comment">// ...</span> <span class="token function">it</span> <span class="token punctuation">(</span> <span class="token string">"reveals a notification when submitted"</span> <span class="token punctuation">,</span> <span class="token keyword">async</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> FormSubmitter <span class="token punctuation">,</span> <span class="token punctuation">{</span> mocks <span class="token punctuation">:</span> <span class="token punctuation">{</span> $http <span class="token punctuation">:</span> mockHttp <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"[data-username]"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">setValue</span> <span class="token punctuation">(</span> <span class="token string">"alice"</span> <span class="token punctuation">)</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">"form"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">trigger</span> <span class="token punctuation">(</span> <span class="token string">"submit.prevent"</span> <span class="token punctuation">)</span> <span class="token keyword">await</span> <span class="token function">flushPromises</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token function">expect</span> <span class="token punctuation">(</span> wrapper <span class="token punctuation">.</span> <span class="token function">find</span> <span class="token punctuation">(</span> <span class="token string">".message"</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">text</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toBe</span> <span class="token punctuation">(</span> <span class="token string">"Thank you for your submission, alice."</span> <span class="token punctuation">)</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Now the test has passed. The source code for flush-promises
about 10 lines of code, if you’re interested in Node.js it’s worth reading and understanding how it works.
We should also make sure the payload endpoint is correct. Add 2 assertes to our test file:
1 2 3 4 | <span class="token comment">// ...</span> <span class="token function">expect</span> <span class="token punctuation">(</span> url <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toBe</span> <span class="token punctuation">(</span> <span class="token string">"/api/v1/register"</span> <span class="token punctuation">)</span> <span class="token function">expect</span> <span class="token punctuation">(</span> data <span class="token punctuation">)</span> <span class="token punctuation">.</span> <span class="token function">toEqual</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> username <span class="token punctuation">:</span> <span class="token string">"alice"</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> |
Our test still passes.
Conclude
In this section, we saw how to:
- use
trigger
for events, even events that use modifiers likeprevent
- Use
setValue
to set the value of<input>
usingv-model
- Write your tests in 3 unit tests
- mock a method attached to
Vue.protopyte
along with usingmocks
to bind them - How to use
flush-promises
to solve the problem of resolving allflush-promises
, a useful technique in unit testing
The source code for the source code is described here .