Working an application in Vue.js with TDD — An extensive guide for people who have time — part 3

Finishing our components and integrating the store

Daniel Kuroski
magnetis backstage
Published in
9 min readOct 26, 2018

--

This is the third in a series of articles:

If you want to read it in pt-BR, check it out here.

Last week we ran simpler tests related to the UserView component. The most interesting of that was the refactor we made, creating a simple base that will be used to test any component in this project.

Let us now delve into and finish all the component tests, and integrate all that with the store.

Integrating the store on our tests

First, let’s change our state file: src/store/state.js

export default {
user: {},
}

Let’s create a fixture file that will contain an example of how our real data would be to be used in our tests.

tests/unit/fixtures/user.js
tests/unit/UserView.spec.js

The refactor is a little long. But let’s understand a what has been done here.

With the shallowMount we are only rendering our component, without any third party dependency and any extra configuration.

Then before the refactor our component didn’t have how to use Vuex or any dependency we ended up installing inmain.js

Thanks to vue-test-utils, we have how to create a vue local instance to be sent to our component. This way, in our test we can indicate all the dependencies it uses globally, on a local way.

  • On line 9, we are creating this local instance, doing exactly what it is done on main.js during the Vuex installation
  • On line 13, we create a state variable, which can be modified between the tests
  • On lines 17 e 18, we send to the component our vue local instance, just like a new store (again, this is exactly what has been made onmain.js )
  • On line 28, we put the beforeEach as it is always called between each test, I take the opportunity to “reset” the used variables on the tests for the standard value. This case, we are using our state original file and storing a copy of this file on our state variable between each test
  • On line 37, finally we can start to see one of the benefits of using the build function. Here we guarantee that before running this test we will have a reseted state variable to its original value. Then, we can change its value with our fixture, containing “real” data of our searched user, and ONLY THEN we would make our component’s build.

This way gives us much flexibility because we can control our tests and make changes before constructing the main component to be tested.

And finally, we can remove our wrapper from the test, as on line 41 we want to guarantee our component is sending the user present on the state and not on data anymore.

RED

Now, our test is failing, as in our UserView.vue we are not sending via props to the VUserProfile.vue our store user. Let’s fix it.

src/views/UserView.vue

Now, we insert the mapState and map the user property of our store and send it to the VUserProfile.

GREEN

Success! Now we are guaranteeing that the UserView is sending the correct property to the VUserProfile.

Last test of the UserView

We are with our component almost covered. It’s missing only one functionality.

I’d like to guarantee that, whenever the VUserSearchForm triggered an event submitted that we call it a store action containing the user which was typed on it.

tests/unit/UserView.spec.js

Let’s first focus on the test:

  • First, we create a variable containing the user hoped to be
    sent through the submitted event
  • Right after, we manually emit a customized event of the VUserSearchForm component which is exactly the event it will make in the future
  • With all that, on lines 50 and 51, we hope that the store action called SEARCH_USER has been called (requested) and that an object containing our user has been sent as payload
  • Line 51 may seem odd, but this is the form we have for guaranteeing we are sending the correct payload to the store actions, because we are not calling manually the store method

What we do is calling the store.dispatch and the Vuex internally calls our action for us. And it ends up injecting on the first parameter an object in which we get to take properties from the Vuex. And as a second parameter, it is our payload.

Then we manually need to take the store call, getting the second parameter which is the payload (our username).

Now we can understand how the preparation for the test was made.

On line 1, we are using a very nice functionality of the jest. Basically using the jest.mock, jest will take a file in the same directory of our imported file, and fill look for a src/store/__mocks__/actions.js instead of the original src/store/actions.js

This allow us to create our mocks so that they can be used by the whole application.

It also works for third parts dependencies. It will basically mock all the dependency functions for you.

Then on line 8 we import our store actions which actually is the mock file we will create.

We insert the actions in the store on line 22.

And finally, on line 34 we are resetting among each test, all the mock functions to the original state, so that no test impacts on the result of the others.

RED

Our test is failing. First, we need to guarantee we are working with a mock function.

src/store/__mocks__/actions.js

Here we are basically returning an object with our function ofSEARCH_USER moving back to a “standard” value, which would be a solved promise with our fixture user.

src/views/UserView.vue

Finally, on our UserView, we can hear the events of submitted sent by the
VUserSearchForm and while calling this we can make our action dispatch.

GREEN!

With these tests, basically, we are covering our UserView component. 😄

For our next components, which are only presentation ones, we are going to speed off a little, as we are basically going to test the same things.

I’m going to post the test and production full files directly to save this article from being EVEN longer. But we should follow the same step-by-steps as it has been done before.

Testing the VUserProfile

tests/unit/VUserProfile.spec.js

Here we don’t see anything that new. As the VUserProfile is a presentation component.

src/components/VUserProfile.vue

Here we add only the data rendering received via props.

GREEN

So then, we can see everything moving on. 😏

Testing the VUserSearchForm

tests/unit/VUserSearchForm.spec.js

Again, the tests here are very similar. There is only a piece of good news, which is the last test.

There, we insert manually in the input our searched user and then we trigger the input events, indicating that “we wrote” in the input.

And finally, the click/submit button so that we guarantee that the event was issued accordingly with the input value.

This way, you have already learned the base for testing components in Vue. Is highly likely that it will move like that and doesn’t get much more complicated (there are cases and cases 😅).

src/components/VUserSearchForm.vue

For the production code, we only render the form and on the submit we issue a submitted event with the username payload.

Testing the action

Now that we covered all our test components, we can go to the second part which will be testing our store, the actions and mutations.

There are two ways of testing the store and I’ll bring only one of them. In case you want to know more, take a look at the

’s book, Testing Vue.js Applications.

Let’s start by testing our action.

tests/unit/actions.spec.js

In this case, we have only one action. Then we make a manual call to the method SEARCH_USER with a mocked commit sending our user payload.

On line 20, we have the use of a dependency which we downloaded in the beginning of the project. It guarantees that all the promises have been solved at this point.

Finally, while calling the action, we hope that our API service has been called in the function searchUser and that it our hoped user has been sent.

Also, as we are testing only the happy path in which the request was made successfully, we hope our commit has been called with our fixture, where it represents an answer mock of the Github API.

src/__mocks__/api.js
// src/api.js
export default {}

As we are using the jest.mock('@/api.js') we need to create our mock file. Later one we are going to create our request service but for now, for our tests, we can have our production file only as the export default.

Not to make our article even longer, I’m going only to test the happy paths. We are not going to cover alternative ways here, e.g. a request fail, network error, etc.

Well… we have just arrived at the phase:

RED

Basically, our method still doesn’t exist. We can finally make our SEARCH_USER production implementation.

src/store/actions.js

We can make the production code in many ways. In this case, I opted for returning a promise.

And here, we make everything we are demanding in our test. First, I’m calling our api.searchUser, sending the username which is being used as a parameter.

Right after I have the answer, I’m making the commit of a mutation, sending our user and returning the API service.

GREEN

Perfect! All the tests passing.

Testing the mutation

The mutation must be the simplest case we have so far.

tests/unit/mutations.spec.js

Here, there isn’t any secret at all. Before each test, we reset our local “state” and what we are doing is calling our mutation directly, sending this state, passing our user.

In the end, we hope that the state had been assigned as being our user, and here I want to make sure that it is a pure function. So, I want to guarantee that the state value is a copy of the original user.

RED

Now that our test failed, let’s go to the implementation.

src/store/mutations.js

Done! I know that in the case of Vue.js I shouldn’t worry about making a copy of the object, the way it deals with reactivity, but I personally like to keep my mutations as pure functions. There wouldn’t be any problem if you do something like: state.user = user .

Just don’t forget to change the test in case you want to do this way.

GREEN

Our test has passed 😄

Overview

summing up

In this third article, we did:

  • Completed all component testing
  • Integrated the tests with the store

Stay tuned to next week we will be finishing our component with store integration.

Thanks for the attention, if you liked, please click on the 💚, and any questions, suggestions or corrections feel free to send me a message, I thank you very much 😄.

My Twitter: @DKuroski

See you next week 😄

--

--