In this post we create a Vue.js app which uses Vuex for state management. The app simply renders two components; one is a link which when clicked, reverses the string it displays; the other, a list of items which we emit from a faux-eventsource. We also explore a method for mocking out parts of the store for better isolation when testing individual components. You can view the live demo here, or alternatively, check out the repository.
Vue.js is a brilliant front-end framework and has a very active and vibrant community supporting it. It also encapsulates the best of angular and react. The Vue.js documentation is up-to-date, extremely beneficial and with the help of one of the CLI templates, it is easy to get an app up and running within a few minutes.
The Vue.js docs are more than sufficient to get started, so we’ll just quickly run through the relevant commands to set up your project. We’ll make use of the webpack template which bootstraps your app with all the necessary configuration and unit and integration tests.
$ npm install -g vue-cli $ vue init webpack my-project $ cd my-project $ npm install $ npm install vuex --save
When prompted with
Setup unit tests with Karma + Mocha?, choose
Y. We’ll be
writing some tests later.
Before we run the app or any tests, there are a few more steps we need to do;
We need to install
babel-runtime to make full use of ES6
babel-runtime is required for building and running our project with
ES6 syntax, and
babel-polyfill is needed by Vuex for running tests.
$ npm install babel-polyfill --save-dev $ npm install babel-runtime --save
karma.conf.js needs to be told to execute the polyfill:
Our project is now configured for ES6 and Vuex, so we can run the tests and the app.
$ npm run unit $ npm run dev
As with the Vue.js docs, the Vuex docs are perfect to get you started. We’ll just add the relevant code.
src directory of your project, create a new dir called
index.js file and an
api.js file. The index.js is the store itself,
whilst the api.js is our event emitter that emits random strings generated with
node’s crypto module. It is designed to simulate real server side events.
In order for the components we’ll create to access the store, we need to add it
to our component definitions. The webpack template creates an entrypoint at
src/main.js which defines the primary Vue component under which all our
components will fall.
We could add the store to each component we create as needed, however, for brevity, we’ll add it once to our primary component:
We now have a store which defines an action to reverse a value and call an arbitrary API, and an action which connects to an eventsource and listens for updates.
Next, we’ll create a couple of components to consume the methods we’ve created.
src/components folder, delete the
Hello.vue component and create
The Reverse component renders an anchor which, when clicked, should dispatch a call to the store we created previously and render the new value once the stores state has updated.
The List component simply renders a list of
items from our store. As the store
receives updates from the eventsource, the List component should render the new
We need to tell our App component to render the two new components, and also that the Hello component no longer exists.
<hello /> with
<reverse /> and
<list />. And, in
the component list, remove
Hello and add
When we run our app, we should be greeted with a link which says “foobar”, and a list of random strings which is updated every few seconds. Click the link and watch the the text reverse itself!
$ npm run dev > [email protected] dev /home/samples/vuejs-sample-1 > node build/dev-server.js Listening at http://localhost:8080
Lets quickly create our two files where we’ll be defining our test cases. In the
tests/unit directory, create
List.spec.js with mocha
describe hooks. If you’re unfamiliar with testing with Mocha and the hooks it
exposes for you to use, have a quick look through their documentation.
For the purposes of this post, we’ll be testing that updates to the stores state reflect correctly in our components. It is important to understand the relationship between the components and the store, and how this affects our test cases we’ll be writing.
Both components depend on a store for managing their state, and in turn, the stores actions depend on either an API request or an eventsource. Both of these dependencies are intractable. They are out of our control and we cannot know with an absolute certainty what the data the store receives will be.
Problems when attempting to test components with these types of dependencies can include the test runner timing out, and undesirable data returned or emitted.
To demonstrate this, we can write a test case which waits for data to be emitted
before we run any assertions. Add the following test case to
With this case, we want to wait for the eventsource to emit a string, at which
point we’ll test that the computed function
items returns the correct amount
and that the generated html is correct.
There are two immediately obvious pitfalls to this approach; We’re relying
directly on the eventsource for updates to the state, and we have no way of
knowing what the emitted string will be. For the latter, we cannot reasonably test
that we’re rendering the string correctly. If we execute
npm run unit a few
times, there is a strong chance that the tests will fail. This is due to our
event emitter being designed to emit an event at random intervals between 1 and
5 seconds - Mochas default timeout is 2 seconds.
Now that we’ve seen how fickle our tests can be, lets have a look at how we can mitigate these problems. We’ll start with the Reverse component.
We know that the Reverse components initial state does not depend on any changes
to the stores state. This makes it deceptively easy to test initially. We can
add the following test case to
Reverse.spec.js and run our tests. Everything
should pass and Mocha should be happy.
Note, because we’re testing an individual component in isolation, the store is not injected into that component via our global App component. Luckily, we don’t need the global component, we can just inject it directly.
If we want to test updates to the DOM and component state after the user clicks “foobar”, we might find ourselves writing some sort of loop-timeout combination which essentially creates a race condition that waits for a server response. This might work, but we’ll never be sure that it will always pass.
Sinon provides some relief, and thankfully, the webpack template
we used included sinon and
karma-sinon-chai which exposes
sinon spies and stubs.
To resolve the issue of asserting DOM and state changes after some asynchronous update, we can mock out the portions of the Vuex store that we rely on. In this case, we rely on the REVERSE action which executes an http request. We can mock this action to update the stores state after some duration:
Sinon replaces the original REVERSE action with the function we just defined. This is useful because we can now control and guarantee the data we’re using to update our state with. We can now mount the component, call the method which triggers the stores REVERSE action, and run our assertions:
Because we control the data flowing back into the store, we can assert with confidence the actual value of the data. The tests should run and return no errors now.
We can use the same pattern to control the flow of data for the component which
relies on an eventsource. Remove the test case we added to
earlier, and replace it with the following:
Again, we mock out the CONNECT action to execute some logic which is
completely in our control allowing us to assert against both state updates and
DOM updates (by waiting on
We have seen that building an app, albeit very simple, can be quick and fun. Most importantly, with minimal effort, we can write effective and trustworthy tests for the components we build.
I hope that this post has given you a foundation to building apps with Vue.js and Vuex, or any other state management framework.