Test your VueJS + TypeScript application; part 2
--
On the last section, we started to test Vuex stores. More precisely we tested the “mutations” and the “getters”. Now let’s focus on the last part of the store; the “actions”.
“Actions” are a bit less straightforward to test than “mutations” and “getters”. By essence, they contain more complex operations; like calling external APIs, calling several commits or actions, and they are promised based. This requires more complex and advanced features of Jest.
Testing an action
Calling an “action” in test context is similar to calling “mutations” and “getters”. There is a slight difference, there is a need to cast the call as “ActionHandler” class from “vuex” module (because actions can have two types, one of them not being callable).
The “action” has one to two parameters. However, it needs to be called with the “store” as “this” object. I use the actual store, because the “context” will be the object called and where the expectations will be checked. It could be better to have a mock but as far as I am aware it is not used directly, and only the “context” object is called.
Here is how the “context” object looks like:
Note that usually, “actions” are promised based. So we need to use the promise test utility from Jest. Here some code example:
Given the “action” is a promise, you will need to make sure it is finished before your expectations are executed, otherwise they may fail because some of your code has not been called yet. If you don’t have other expectations and just want to check the result of the promise then returns it so that Jest waits for it to end before ending the test.
The good things about the ”resolve”/”reject” matchers of Jest, is that it also returns a promise so you can “await” it before running the rest of your test (note: using “await” require that you put “async” to your test function).
Checking commit and dispatch calls
An “action” calls “commit” to call “mutations” and “dispatch” to call “actions”. Those are handled through the “context” object. So there is a need to have mocked functions; to create mock function with Jest: “jest.fn()”.
It can be argued that unit testing is about the outputs. However, most of the times, output of an “action” is not relevant as its primary goal is to modify the store through “dispatch” and “commit”. So I would consider them as outputs as they are causing side effect on the store, and we would like to make sure those side effects are controlled and called as expected.
First thing that can be done is actually make sure the “commit” and “dispatch” functions have been called the appropriate number of times.
After that, we can check the content of each call. The mocked function provides utilities to check which arguments have been passed. There is a “mock” property for the mocked function, which in place contains a “calls” property which is a two dimensional array. In this array, the first dimension is each call of the function, and the second one is the array of parameter.
Mocking dependencies
As stated above, “actions” contain more advanced behavior than “mutations” and “getters” that we saw previously. They are calling APIs, calling complex business logic, etc. And usually, all of this comes from dependencies, whether they are internal to your code base, or third party. In any case, we need to make sure those dependencies behave as we expect them for our tests. That’s where “mocks” comes handy (if you don’t know what a mock is, take a look at the Wikipedia article).
Jest provides a way to mock dependencies, so no need to another third party test library. Let’s take the case where the dependencies comes from our code base. I have a “DialogFileService” class that calls native file dialog through Electron framework. One of the tested “action” uses it, so let’s mock to achieve the tests.
First of all, let’s import it inside the test file.
After that, as we are relying on TypeScript, we will need to use utility from “ts-jest” so that the mocks play nice with typings. This utility will help us to create the mock that works with TypeScript.
When you create your mock with the “mocked” function, the first parameter is the object or class to mock, and the second parameter is whether it is a deep mock or not. It is safer to go for “deep” as everything will be mocked.
You can now mock methods of the object to replace them with the implementation specific to your test. The nice thing with “ts-jest” is that it will match the typings against the real implementation. So if your mock is not compatible with the real implementation then it will complain, highlighting you potential mistakes more easily.
For third party dependencies, the job is pretty much the same. Let’s try with the “axios” library. It’s a library providing promise based function for XHR calls, so you’re likely to use it or use a similar one if you are calling web APIs.
Let’s do the import, and do the mocking.
And then you call it the same way:
You can also use the mock in your test expectations:
Finally all set together, it gives those final implementations
You should now have tips on how to test your own stores. If you have questions or remarks, don’t hesitate to comment.
On the next one, we will see how to test a simple component.