Tips for Unit Testing Vue Components with Jest

Achhunna Mali
May 19, 2019 · 6 min read

Using Jest to unit test Vue.js components can be tricky. We need a separate Vue Test Utils (@vue/test-utils) scoped package in order to virtually mount our components and use Jest to execute the tests. Because these two libraries work together, it’s important to make sure we don’t get confused about which API calls belong to which library. Along with these libraries, we also need to pay attention to JSDOM (virtual browser environment) specific methods, which comes with Jest. Having to juggle all this may be confusing and can discourage us from writing unit tests.

May feel like a juggling jest(er), Credit: https://dribbble.com/fedecook

E.g., shallowMount() is a Vue Test Utils method to create a shallow wrapper component (using shallow rendering to avoid rendering children components), and beforeEach() is a Jest method which executes the callback argument before each of the tests. We run shallowMount() inside beforeEach() so a component is mounted before every test.

I made a list of common testing methods that my team used to help successfully unit test our Vue components. Hope this will help others going through similar process.


Initial Setup

Let’s start by setting up Jest. Install Jest npm package as devDependencies

$ npm i jest -D

Now let’s install Vue Test Utils and other dependencies like babel-jest, vue-jest etc. (this is straight from the Vue Test Utils docs)

$ npm i @vue/test-utils vue-jest babel-jest -D

Once we have all packages setup, create a Jest config file jest.config.js in the root folder of our project. As an alternative, we can also add JSON object inside module.exports to a jest: {} property to package.json which cuts down on the number of config files we have to manage.

Make sure .babelrc file has config below with babel-present-env module installed.

Once we have setup the configs with options (mainly collectCoverageFrom source path) we are ready to run Jest!

Now all we need to do is add scripts: { “test”: “jest” } to package.json. Then run that script.

$ npm run test

Writing Test Files

Jest will pick up *.test.js or *.spec.js files in our project folder. I like to place test files in the same folder as my Vue components and use component file name. e.g. button.vue component will have button.test.js test file in components/ folder. This helps me manage component files and test files since they live the same folder and are right next to each other in the Explorer in VSCode.

Test File Template

Jest has describe(), test() and expect() testing functions to setup each test and asset expected value. I like to check wrapper.isVueInstance test passes in the beginning to make sure I have mocked default properties for the component.

Sample Jest test template

If our component uses Vuex, we need to add Vuex to a Vue instance (named localVue here) using use() and pass the instance into shallowMount().

Template with Vuex mocks

Common Testing Methods

Credit: https://www.behance.net/taylorrafael

Let’s look at some testing methods that cover most types of unit testing.

  • Existence of DOM elements
    We begin the test by checking if wrapper has all default elements we expect to render when component is mounted. We do this by checking for actual element tag or other attributes like class, id etc. Jest’s assertion functions like toBe() along with wrapper.contains() or wrapper.findAll().length from Vue Test Utils can help with this test.

We can also check for innerHTML of elements using .text() function of the DOM element that wrapper.find() returns.
e.g. expect(wrapper.find('.blah').text()).toBe('blah text').

  • DOM action events
    We can test the events that should emit when an action is performed in the component. e.g. when a close button is clicked, a dialog should be close by listening to a close event. We use trigger() function from Vue Test Utils to trigger an event on the wrapper’s selected DOM element and wrapper.emitted() gives a list of emitted events which is used to check for existence desired event.

Above we trigger click event on closeBtn DOM selector. We can also trigger other events like keydown for keypress etc.

  • Accessing Vue wrapper properties
    In case we need to access or change data, computed, methods and props of our Vue component, we can usewrapper.vm object since Vue Test Utils creates an instance of the component in wrapper. This is helpful when we have to mock values for one of the properties.
wrapper.vm.name = 'test'; // changes name data propertywrapper.vm.save(); // invokes save() methodwrapper.setProps({ propsName: newValue }); // changes propsName to newValue

One exception is computed properties, which need to be remounted using shallowMount() and passed in as a property. Since we mount our component in beforeEach(), we can store the shallowMount() in a factory function which can be easily called during beforeEach() and in test that needs to pass in overrides for computed properties.

  • Mocking methods and modules
    One of the important things to know when writing unit tests is how to fake or mock the execution of a module or method that are not part of the unit we are testing. This helps make our tests more predictable and also avoids any external code changes from failing our tests. We can use Jest’s fn() and mock() functions to return mocked versions. There is also a mocks property inside shallowMount() in Vue Test Utils which is use to mock any global functions that our component might be using without import statement.

Let’s look at mocking functions.

Mocking methods using jest.fn()

Above test, we use jest.fn() to mock the return value of various functions. We can pass an argument inside fn(() => true)that gets invoked for the mock. If left empty, an undefined function is returned.

We also mocked window.open() global function because JSDOM doesn’t provide but our test needs to execute it.

These functions are also spies which lets us spy on their behavior and use expect().toBeCalled() functions to verify they are invoked in the test. We can also use jest.spyOn(Object, function name) Jest function for similar behavior.

Other helpful function mocks include setInterval and setTimeout since Javascript native timers don't work as expected in Jest. We can use Jest’s functions like advanceTimersByTime(x) or runOnlyPendingTimers() to clear all timers, just make sure to declare jest.userFakeTimers() at the top of test file.

jest.useFakeTimers(); // Declare at top of test filejest.advanceTimersByTime(1000); // Inside test function if you have specific millisjest.runOnlyPendingTimers(); // Fast-forward until all pending timers have been executed

Mocking imported module is quite simple.

Mocking modules using jest.mock()

If we have a Constants module imported in our component, then we return the mocked object with any properties our component uses, e.g. Constants.myConstant.

  • Async callbacks
    If we have asynchronous code in the code block of our component, we can use async/await to await the async callback, usually a Promise.

Demo


Conclusion

The Startup

Medium's largest active publication, followed by +608K people. Follow to join our community.

Achhunna Mali

Written by

Thoughts on technology & design, achhunna.com

The Startup

Medium's largest active publication, followed by +608K people. Follow to join our community.

More From Medium

More from The Startup

More from The Startup

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade