Unit and Integration tests in VueJs using Jest — Tutorial

Maybe one of the most confusing things I faced when I started writing tests for VueJs components is to find the line between unit test and integration test in terms of implementation. I mean in theory everything looks clear but when you start to write tests for both types it gets mixed up!

The way I think of these tests is like the following: a unit test would be a way to check that the component outputs are correct for an input or set of inputs in an isolated environment from other components . On the other hand the integration test would be a way to test how a set of components talk/interact with each other and how an output of a component can affect other components in the same page/section or any container for a group of components.

So as I mentioned before in theory it sounds good and actually you can find much better definitions than mine over the internet and all of them in one way or another leads to the same idea, but sadly knowing the definition will not be sufficient for writing both types of tests for your application, that was my motivation behind writing this article to show you how I managed to separate the both test types and setup the environment for them.

Getting started

Without further due let’s create a new Vue project using Vue cli, so choose any location on your machine and run the creation command :

vue create vue-integration-test

Choose the default options when prompted also you can refer to the Vue CLI homepage for more instruction and guidance on how to create a new Vue project.

After the above command completes successfully we’ll have a new Vue project that we will build on top of it.

Now we need to install Jest and by the definition on their website it’s

“A delightful JavaScript Testing Framework with a focus on simplicity”,

let’s put that to test and find out.

To install Jest run the following:

yarn add --dev jest

Next we need to install the Vue-test-utils which is the official unit testing utility library for Vue.js as mentioned on their website and the vue-jest transformer which will handle the *.vue files.

yarn add --dev @vue/test-utils vue-jest

Great! Now we have all that we need to write both unit and integration tests.

Setup Jest

I think the Jest setup is the trickiest part and that because we need to have two slightly different versions of Jest configuration for both unit and integration tests so we need to find a way to have separate config for each of the test types, by default Jest uses `jest.config.js` file which holds the config as the name implies.

The idea is to create two config files one for each test type, so let’s create two more config files one for unit tests which would be `jest.unit.config.js` and the other one for integration tests and it would be `jest.integration.config.js`.

Now let’s go back to the main file `jest.config.js` and see what’s inside, basically by default this file might not exist so we need to create it and add the simplest config for Vue testing as stated on Vue-test-utils website.

// jest.config.jsmodule.exports = { "moduleFileExtensions": [   "js",   "json",   "vue"  ],  "transform": {    ".*\\.(vue)$": "vue-jest"
}
};

Now let’s update the other jest config files, let’s start with `jest.integration.config.js` mainly we want to add some config to make jest match only the test files under `__integrration-tests__` and that can be achieved simply like the following

// jest.integration.config.jsmodule.exports = {  testMatch: [ “**/__integration-tests__/**/*.[jt]s?(x)” ],}

So by using the `testMatch` option we inform Jest to look only for test files that matches the provided pattern, and in our case it’s any test file lives under directory named __integration-tests__

That’s it for integration tests config, let’s update the `jest.unit.config.js` with the same configuration but will match test suites under __tests__ so the config file should look like this

// jest.unit.config.jsmodule.exports = {  testMatch: [ “**/__tests__/**/*.[jt]s?(x)” ],}

Great! Now we need to find a way to load each of these config files with the corresponding test type! To do that we need to do a small modification on the main config file `jest.config.js` so it will look like this.

// jest.config.js// loading the corresponding test config.const config = require(`./jest.${process.env.TEST_CLASS}.config`); module.exports = {“moduleFileExtensions”: [“js”,“json”,// tell Jest to handle `*.vue` files“vue”],“transform”: {// process `*.vue` files with `vue-jest`“.*\\.(vue)$”: “vue-jest”},…config // adding the config to the mail config object.};

Great! But what was that modification ? Basically we are expecting to have the test type as an environment variable under the name TEST_CLASS and depending on it’s value Jest will load the corresponding configuration and add it to the main Jest config object.

Now to run the unit tests we use:

TEST_CLASS=unit jest

And to run the integration tests we use:

TEST_CLASS=integration jest

Both of these command can be added to package.json file under scripts like the following:

// package.json"scripts": {
...
"test": "NODE_ENV=test TEST_CLASS=unit jest", "integration-test": "NODE_ENV=test TEST_CLASS=integration jest"},

So now we can simply use yarn test to run unit tests and yarn integration-test to run integration tests.

Congrats! Now we have a working Jest setup for both unit and integration tests.

Let’s get our hand dirty by writing some tests for our demo app.

Demo app

For the purpose of this article I created a very simple Vue app which consists of a page component (Home.vue) with two nested components the first is the Login.vue which is a simple login form, and Status.vue which is a component to display the current login status.

You can download the project form Github

Writing Unit tests

Let’s start with unit tests as this type is simpler and it forms the base for the integration tests. There are multiple ways to write unit tests. The one I find the simplest is by defining inputs and outputs of a component.

Define Inputs

In unit test context the inputs of a component are any interactions made by the user which changes the state of a component and by the state I mean any data props, template change, Vuex state change and others. In our case we will define the inputs for the Login.vue component like the following:

  1. Click on a login button.
  2. Click on a reset button.

Define outputs

In unit tests context, the outputs are all the possible outcomes from the defined inputs in the scope of the component, for example when clicking a button which changes a data prop value to true, or emits a value to outside for a component.

A rule of thumb to define the outputs in unit test is to only test the changes in the same component not outside it neither insied it (Nested components).

For example if the change in prop value suppose to do something to a nested component like opening a modal then we don’t test that behaviour since we only care about the scope of the component in question so we only need to test that the prop value is changed.

In our case for Login.vue component the outputs groups by inputs will be as the following:

  1. Click on a login button.
    a. Should emit `failed` if the login failed.
    b. Should emit `success` when login succeeds.
  2. Click on a reset button.
    a. Should clear all form inputs.
    b. Should emit `idle` status.

Now each of these outputs should be a separate test case like the following.

Note that we use shallowMount to create the wrapper and that’s because in unit tests we want to render only the component itself without any other compoents to be able to test it in isolation.

Now to run the test use the command we defined previously:

yarn test

Awesome! now we have a working unit test setup.

Writing integration tests

Now it’s the time to write our first integration test

We will use the demp app to demonstrate how we can write integration tests for the Home.vue and it’s nested components.

To write an integration test we need to find all the inputs and outputs of a component which affect other sibling components.

Define inputs

Inputs are all the interactions the user can make with the UI for example clicking a button, hovering over a tooltip, filling a form and any other type of interaction that we record or use in our app.

Now looking at our Home.vue we can find the following inputs:

  1. Click on the login button.
  2. Click on the reset button.

Define outputs

Outputs are all the possible outcomes from the defined inputs, for example clicking a button can change a text colour and make a XHR request in this case it has 2 outputs. An input should have at least one output otherwise it’s not an input. For our demo app the outputs grouped by inputs will be :

  1. Click on login button
    a. Should change the status text to `Success` if login succeeds.
    b. Should change the status text to `failed` if login failed.
  2. Click on reset button
    a. Should reset the status text.

Note that in integration test we don’t care what an input do to it’s own component we want to see how an input affects other components and test that.

The same as unit test example, each output should be a separate test case .

Note that we used mount this time to create the wrapper and that’s because we want to render everything under the component and that includes the login.vue and status.vue, this allowed us to test the interactions between components.

Now to run the integration tests, use the command we defined previously:

yarn integration-test

Awesome! Looks great.

Conclusion

Unit test and integration test can be tricky and overlaps easily in VueJS components, the key takeway is that in both unit test and integration test the inputs could be similar but the outputs should always be different, where the first should include test cases for the changes in it’s own scope while the second should include test cases for changes outside of it’s scope, if you found that you are duplicating your self then one of the defined outputs (test cases) in either the unit or integration tests is not in the write place.

I hope this article helped you getting some idea about VueJS testing and Jest setup, feel free to ask any question in the comments section I’ll reply ASAP.

PS: Don’t forget to clap if you liked the tutorial. Thanks.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store