Vuex + IDD: Part1

Frontend with fake backend

Alberto Gualis
Coding Stones
8 min readApr 25, 2017

--

This is the first article on a series about unidirectional data flow architecture based on Vuex and IDD.

In this post we’ll focus on achieving fast feedback for the TDD workflow on frontend.

At the beginning of April, we travelled to Gran Canaria to attend the amazing Socrates Canaries (AKA #Socracan17), where we shared our latest spike with Vue, Vuex, and IDD.

Some of you were interested in the unidirectional dataflow approach of Vuex, while others were more interested in the global architecture and how we bind the frontend actions with the backend ones. That’s why we’ve decided to blog a bunch of posts, from simple to advanced, so you can download the code and play with the parts that you prefer.

Testing Tools

One of our main goals was to check how easy was to test the logic and components in a Vue project.

As TDD practitioners, we needed the tests to be very fast to have quick feedback cycle. We like Mocha because it allows us to directly transpile our ES6 code through babel and directly run our tests in node. This way the tests don’t depend on Karma, browsers or webpack bundles like in the common examples found on the web. All you need is to have this line in your package.json scripts:

"unit": "mocha --compilers js:babel-register --require babel-polyfill --recursive 'src/{,!(router)/**/*}.js'"

Then you can run our unit tests with:

npm run unit

Note that we are using the special expression {,!(router)/**/*} to ignore the router folder as it is the only one with references to .vue files which babel compiler would not understand. (In future posts we’ll see how to test this files with integration tests).

We also use Chai for assertions and Sinon for doubles as we’ll see later on.

Where is the backend?

In this first example the backend does not exist. We have created a fake implementation (check sessions-api-service.js) for the sake of simplicity.

You’ll have to wait to see the real backend implementation in future posts

Starting a Vue/Vuex Project

We’ll start with a very simple Vuex example where we’ll show why it seems to be the framework/library that best fulfills our needs. We don’t pretend to create a Vuex tutorial because the official documentation is brilliant and you can also find some very good post talking about it. However, we feel that we have some ideas that could be interesting if you are looking for very fast and vue-agnostic tests and you don’t like to get married with frameworks. We like to invert efforts in crafting code that gives you independence and quick feedback.

The spike is a very simple web that shows a list of sessions (talks) and allows us to add new sessions through a validated form. We’ve deployed the here.

You can find the code of this post in this repo:

The first version is a dummy list with two sessions or a fake server error if you are not lucky (refresh until you get them).

We created our project using the typical vue-cli options:

Typical Vue CLI options to start a Vue project

Then we added Foundation Framework dependency because we use it in some of our projects and wanted to check how to integrate it with Vue and webpack. We can say that Vue generates very nice webpack setup files and it makes very easy to integrate with any SASS/LESS/CSS solution. In this case, we’re just using the SASS part of foundation and avoiding the JS (jquery) integration to make the example simpler. If you want to try a more complete integration, this post will be very useful.

From this starting point we need to install vuex (npm install --save vuex) and add our first actions. You should have heard about flux/redux unidirectional dataflow architecture. We weren’t very familiar with it but after having a very inspiring talk with Ricardo Borillo we decided to give it a try.

We must admit that we were worried about the steep learning curve of this architecture but after this spike we found Vuex a very simple and clear approach if you compare it with other options.

Testing Vue

In Vuex we have 3 main pieces: store (single point of truth), mutations and actions.

Testing Mutations

Committing mutations is the only way to actually change state in a Vuex store. Mutations are pure functions that receive the current state and an optional payload so we should be able to test them with single unit tests.

Our first version of the app is just a single Vue component that renders a list of sessions when it is loaded. We are using a centralized state management pattern so the sessions should be present in the store (single point of truth). In real life, the frontend should request this list of sessions to a server when needed. What happens while this request is being processed? The frontend should show the loading state in the UI. What happens when we successfully receive the response with the list of sessions? The frontend should show the new list of sessions in the UI. But, what happens if there is some error ? The frontend should show an error state in the UI.

We can then divide this process in three different steps (mutations) that can lead to different statuses of the store.

fetchSessions: we are in the process of requesting the sessions (component shows loading state from the store in the UI)

fetchSessionsSuccess: we successfully got the sessions from the server (component renders sessions from the store in the UI)

fetchSessionsError: something unexpected happened (component shows error state from the store in the UI)

Let’s see the tests for this mutations:

Mutations are pure functions that can be easily tested

Easy, right? For now, we decided to have a global error and loading variables that will be used by the components to render those states in the UI. Then our store is as simple as this:

const initialState = {
sessions: [],
loading: false,
error: '',
};

When the app starts growing we’ll probably need to refactor. But for now it’s enough (we love baby steps).

Testing Actions

What about testing the actions? From Vuex docs:

Actions are similar to mutations, the difference being that:

- Instead of mutating the state, actions commit mutations.

- Actions can contain arbitrary asynchronous operations.

In our example we already have 3 mutations that can be committed and we already know that we should interact with the server (asynchronous operation) to retrieve our sessions. All these elements should be managed by our first action. The best way to explore what this action should do is through a test.

The official Vuex docs recommend to use Webpack with inject-loader and provides a helper to mock commits. Do we really need this? Let’s see if we can find a better way. We will normally need to do two things when we test an action:

  1. Verify that we have/haven’t committed one or multiple mutations
  2. Verify or mock the interaction with external API’s

What if we could double Vue’s commit mechanism so our action tests were totally independent of Vue?

What if we could mock the dependencies with the external APIs?

For Vuex, an action is just a function that receives an object with its parameters:

function vuex_action({ commit })

Then we “declare” this functions in the actions definition inside the Vuex.Store object and import them as methods in the Vue Components thanks to the …Vuex.mapActions helper (check the repo for more detail).

Thanks to Vue’s simplicity, sinon doubles and dependency injection we could test our first action like this:

Many important things to understand in the tests above:

Vuex references mutation functions with constants and commits (dispatches) mutations passing its type (constant) and an optional payload.

fetchSessions() -> FETCH_SESSIONS (no payload)fetchSessionsSuccess() -> FETCH_SESSIONS_SUCCESS (sessions list payload)fetchSessionsError() -> FETCH_SESSIONS_ERROR (error string payload)

Importing these mutation type constants and mocking Vue’s commit parameter is enough to verify if the action committed a mutation in each context.

We are using sinon.stub() to double the implementation of the Vue commit engine in the tests (Vue will use the real one in production code).

We are using a builder function (retrieveSessionsAction) to create our action in a way that we can inject a collaborator (sessionsApi). This collaborator object is responsible for the interaction with the backend.

But the calls to the backend should be asynchronous, where is the asynchrony in the tests? We created two test helpers (resolvedStub and rejectedStub) using the magic of sinon-stub-promise. This way we can stub our sessionApi methods returning synchronously resolved promises without typing too much code. Did we say that we like simple things?

Our tests are flaky!

We wanted to simulate some errors during the connection to the server so this is the implementation of the method that retrieves sessions:

This means each time you reload the page you have 30% probability of seeing SERVER ERROR: Connection problem in the UI. One test will also fail 30% of the times. Keep calm, we will fix this in future posts when we add the real implementation of the server.

Creating a very simple Vue component

The last thing to explain is that we did not want to add cognitive load creating a complex vue component. If you check Sessions.vue file you will see things like this:

<h2 class="title error" v-if="loading">LOADING...</h2>

<h2 class="error" v-if="error">SERVER ERROR: {{ error }}</h2>

This is our cheap way of rendering LOADING/ERROR status in the UI. Of course, we will improve this in the future.

Notes for Redux users

If you know Redux you will probably miss immutability in the changes of the store.

In Vuex the store is reactive, so we just need to use the Vuex.mapGetters helper to register the needed store parts as computed properties. This way the component will automatically re-render the UI when they change.

During the Socracan17, we also attended to a great Redux session facilitated by Carlos Ble and we recommend this posts by the @Codesai team if you are more into React.

In future posts

Before going deep into the backend, we will see how to add more components to separate responsibilities in the UI, we will see how to test Vue components taking the DOM into account, we will integrate the router in Vuex and test the interactions with it and we will add a form to create new sessions and see how easy is to validate it.

That’s everything for now. As you can see, we were focused in showing the test code, but feel free to check the github repository to see the implementation details. Also, if you install it to play with it, we would be glad to have your feedback!

If you are new to Redux or Vuex, a nice help would be installing the vue dev-tools extensions, to interact with the app while you see how the state changes through mutations. You can also play with the time machine to better understand this paradigm.

Stay tuned!

At the Coding Stones we help companies developing software and we also provide training and mentoring. If you liked this post and you want more, contact us, maybe we can play together with your band.

--

--