Harry Potter and the Javascript Fatigue — Part 2

Maximilian Schuler
itemis
Published in
5 min readFeb 2, 2018

This is the second part of a series of articles on Vue.JS frontend development. In case you missed it, here is Part 1: The The Philosophers UI

You can pull the App from here! If you do so, run git checkout part2 for this part.

Part 2: The Chamber of Storage

You have a frontend that allows viewing of a static list of Todos. Now you need something to store and change all your magical tasks! You imagine a chamber of secrets. But after some consideration you decide to call it a storage of state, or shorter: store. As it turns out these things already exist (what a relieve). You could use Vue.js directly to manipulate state but there is also Vuex.

Store Skeleton

So you install it yarn add vuex and put it into use

with a store that just contains two tasks

Unit testing user workflows

Now this was easy, but does not solve your problem yet. You need a way to enter the title text of a new task. An input tag should be visible and on hitting enter it should call an Action that in turn mutates the state. You start by writing a test for a new Vue component with an input field.

This test asserts that there is an input field. The component may look like

This is a component that renders an input field with no functionality at all. You want it to add a task to the store once you hit enter. You can test it by simulating a keyup.enter event and verifying that it calls a method called addTask.

To make this test work only two very small changes to the component are necessary.

  1. add @keyup.enter="addTask" to the input field
  2. add a addTask() {} method to the methods block of the component

Ok, this calls a method on the right key press. But you did not specify what the method should do! So you define that it should dispatch an Action to add a task with the given title.

This test assumes that the field taskTitle contains the input fields value. But wait a minute, that is not what you want to test! You want to capture the behaviour of the component and not it's implementation. A perfect test would

  1. set the input fields value to ‘title’
  2. trigger a ‘keyup.enter’ event
  3. check that the correct value has been passed to the store

After some experimentation you finally come up with the following test

So you spy on the actual store (0). Then you set fill the input field with a test valuer (1). To get that value into the components state you trigger an input event. You simulate the browser at this point. Then you trigger the key press of the enter button (3). And finally check that the correct Action triggered in the store.

In step (4) you give Vue.js a tick to sync with the values in the input field. If the implementation does not use watchers this might not be necessary. But better be safe than sorry! And after asserting that the component called the store, you have to report to jest that the test finished (5).

This test can replace all the others. While developing a component it is OK to test implementation details. But after finishing the component, your tests must only cover the intended behaviour. Internal function calls and variables may change and then all your tests break!

Anyway, here is the full component:

Testing store interaction

Up until now, you have not implemented the AddTask Action. And we ignored that this resulted in a console error

It did not fail your tests. At least it did not fail mine. But this is the next thing that you need to put in place.

First you need something to change the state. In Vuex calls this a mutation. A mutation is very easy to test, as it is a function that changes state with no strings attached.

This is pretty easy to implement also

The Mutations name is in the Past Tense. This emphasises that it describes an event that has already happened. The idea is that this event is not reflected in the state yet. This also means that there must not be any circumstance in which a Mutation might fail! A Mutation might be TaskAdded but also TaskAddedFailed. The second mutation describes a failed attempt. But the mutation itself must not fail!

The next thing you want in a store is a predefined function to fetch a task by title.

Testing this getter was easy, but getters might very well refer to each other. Consider these two getters

firstTitle returns the first task of the tasks ordered by title (this code uses lodash). In such a case it makes sense to mock the dependencies

On first glance, testing actions is much harder than getters or mutations. Actions have much more dependencies and their effects are function calls. It feels easier to check modifications of the state. They cannot change the sate at all! That also means that you do not have to think about how the state changes! It is valid to mock the context (state, getters) and spy on the commit function. You only define a constant state and mock away the getters. Then assert that the action calls the right functions. The only thing that might get complex is that Actions may do all kind of messy asynchronous Api calls (And we will look at that in Part 3 of the series)

Spying on a function is a very powerful tool to verify a lot of interaction patterns. The above code says that it only adds a task if it cannot find it by title. And using the getter you implemented already, this function is

A scalable future

For today you find yourself satisfied by the result. You can view and add tasks! The next thing you want to do is to change the state of your Tasks.

You sketch some types for the Task State logic

Now with these actions and mutations you should be able to implement this in a test-driven way.

You realise how well this approach scales. If you could only convince the Weasleys to learn Typescript to help you out with the new features. But this is something for another day at Hogwarts!

--

--