This document aims to outline some short examples of how to unit test using a Vue project. This guide is specifically designed to be used on the following Vue set up.
With the above setup, you can now start writing Mocha unit tests in Vue. The steps below show how to create tests.
Files that are picked for the testing end with **_.spec.js_**
. You can see how this is set or change this inside **_package.json_**
.
"scripts": {
"test:unit": "vue-cli-service test:unit src/**/*.spec.js"
}
In the terminal type:
npm run test:unit
import Spinner from "@/ui/Spinner";
import AppLoadingScreen from "./AppLoadingScreen";
import { shallowMount } from "@vue/test-utils";
import { expect } from "chai";
describe("AppLoadingScreen", () => {
let component;
beforeEach(() => {
component = shallowMount(AppLoadingScreen);
});
it("should render Spinner on mount", () => {
expect(component.find(Spinner).exists()).to.be.true;
});
});
The numbers below reference the code AppLoadingScreen.spec.js code block above.
**_shallowMount_**
is imported from Vue utils. It creates a [**_Wrapper_**](https://vue-test-utils.vuejs.org/api/wrapper/)
that contains the mounted and rendered Vue component, but with stubbed child components. A stubbed child component is a replacement for a child component rendered by the component under test. Some more details here on how stubbed components work: https://stackoverflow.com/questions/52962489/what-are-stubbed-child-components-in-vue-test-utils**_expect_**
is imported from chai. expect is a chainable language to construct assertions. Assertions are used to test a specific thing in the code. Some examples of what can be chained to expect to test https://www.chaijs.com/api/bdd/6. **_describe_**
is used to outline what you are testing. This will also show in the terminal after running the test. In this case, we are testing the appLoadingScreen component.
9. **_beforeEach()_**
is a Mocha method which executes the callback argument before each of the tests. We run **_shallowMount()_**
inside **_beforeEach()_**
so a component is mounted before every test.
10. **_shallowMount()_**
method is used placing our test component inside.
13. **_it_**
is where you perform individual tests. You should be able to describe the tests, in our case “it should render Spinner on mount”. This clearly outlines what the test will be.
14. **_expect_**
from chai. Asserts that something should be tested. In our case we look inside our component for the Spinner component, by using **_find_**
from vue utils. Find returns a wrapper of the first DOM node or Vue component matching selector. As we are using chai expect, we can chain keywords. In this case, we add **_to.be.true_**
Running the above test **_npm run test:unit_**
Displays the results of the test describe being the “AppLoadingScreen” and it being “should render Spinner on mount”.
A **_.spec.js_**
file that uses vuex
import Vuex from "vuex";
import { createLocalVue, shallowMount } from "@vue/test-utils";
import chai, { expect } from "chai";
import sinon from "sinon";
import sinonChai from "sinon-chai";
import Modal from "@/ui/Modal";
import BaseButton from "@/ui/BaseButton";
chai.use(sinonChai);
const localVue = createLocalVue();
localVue.use(Vuex);
localVue.component("BaseButton", BaseButton);
describe("Modal", () => {
let store;
const getters = {
isModalOpen: () => true,
activeModalName: () => "baz"
};
const actions = {
TOGGLE_MODAL: () => true
};
let component;
const mockMethod = sinon.spy();
beforeEach(() => {
store = new Vuex.Store({
getters,
actions
});
component = shallowMount(Modal, {
store,
localVue,
});
});
describe("can close when", () => {
it("clicking 'x'", () => {
component.setMethods({ TOGGLE_MODAL: mockMethod });
component.find(".modal-close-btn").trigger("click");
expect(mockMethod).to.have.been.called.calledWith({
isOpen: false,
name: null
});
});
});
});
The numbers below reference the code Modal.spec.js code block above.
vuex
as we are going to create a store inside the test file.createLocalVue
from vue utils. Is used to create a local class of vue so we can use components, plugins and mixins without polluting the global vue class.4. sinon
is imported. sinon allows you to create test doubles. For example mock methods from the component we want to pull into the test.
5. sinon-chai
is imported. Extends Chai with assertions for the Sinon.JS mocking framework.
6. The modal component is imported (the component we are testing)
7. BaseButton is imported — we have to import this component as it is registered globally.
8. chai.use
is called and set to use sinonChai
11. createLocalVue
is stored in a variable for reuse.
12. Set the localVue
to use vuex.
13. Register the BaseButton component as it was previously registered globally.
16. Create the store variable.
17. Create the getters, matching the component getters. ( here we can change the values to help us test, for example, if something is hidden in v-if we can change to true in the test so it becomes available)
18. Create the actions, matching the component.
25. store a mockMethod
using sinon.spy()
. A spy call is an object representation of an individual call to a spied function, which could be a fake, spy, stub or mock method.
28. A new store is created passing the getters and actions.
33. We create the shallow mount version of the component with the localVue and store passed in.
41. setMethods
allows you to use methods from the store, in this case, we are using the action we created, matching the component file action TOGGLE_MODAL
. https://vue-test-utils.vuejs.org/api/wrapper-array/#setmethods
42. find
is used to search for a selector in the component. From vue Utils https://vue-test-utils.vuejs.org/api/wrapper/#find
42. trigger
is used to pass in the event, in this case ‘click’ is the event that would fire on this selector. https://vue-test-utils.vuejs.org/api/wrapper/trigger.html
43. Here is the test. We use expect
to assert that the mockmethod
to.have.been.called.calledWith
chainable methods come from chai and can be used to test a variety of scenarios. calledWith
can check the see what arguments have been passed in, this way we can be sure that what gets passed in does not change otherwise the test will fail and indicate why.
That’s a basic unit test using mocha and Vue.
As you can imagine each component may have different logic in with new test syntax needed. Vuex is commonly used in Vue apps, which adds another layer of complexity when testing.
With this test, we can check that a store action was called, by testing what would happen if we triggered a click event on a selector.
The store logic itself can be tested independently on its own, which would give you a detailed test of what each action and mutation are doing.
#vue-js #vuejs #testing #javascript