How to unit test your first Vue.js component (2024)

/ #JavaScript
How to unit test your first Vue.js component (1)

by Sarah Dayan

In Build Your First Vue.js Component we made a star rating component. We’ve covered many fundamental concepts to help you create more complex Vue.js components.

Yet, there’s one crucial point you need to build bulletproof components you can use in production: unit testing.

Why unit test a component?

Unit tests are a crucial part of continuous integration. They make your code a lot more reliable by focusing on small, isolated entities and making sure these always behave as expected. You can confidently iterate on your project without fear of breaking things.

Unit tests aren’t limited to scripts. Anything we can test in isolation is unit testable, as long as you respect a few good practices. These practices include single-responsibility, predictability, and loose coupling.

As reusable entities of our app, Vue.js components are great candidates for unit testing. We’ll test the one we made as a single unit with various inputs and user interactions, and make sure it always behaves as we expect.

Before we start

A few things have changed since the initial tutorial. Vue CLI 3 was released. Vue Test Utils — the official Vue.js unit testing utility library — has matured to beta version. In the first tutorial, we used webpack-simple, a prototyping template that doesn’t include testing features. For these reasons, the simplest thing to do is to wipe the slate clean and migrate the project from the tutorial to a more recent Vue.js install.

I re-created the project from the first tutorial so you can download it directly from GitHub. Then, navigate to the unzipped directory and install dependencies.

Note: make sure you install Node.js before going further:

cd path/to/my/project npm install

Then, run the project:

npm run serve

Vue Test Utils and Jest

For this tutorial, we’ll use Vue Test Utils, the official Vue.js testing toolkit, along with Jest, a JavaScript test runner backed by Facebook.

Vue Test Utils lets you mount Vue components in isolation and simulate user interactions. It has all the necessary utilities to test single-file components, including those using Vue Router or Vuex.

Jest is a full-featured test runner that requires almost no configuration. It also provides a built-in assertion library.

Vue CLI 3 (which I used to generate the boilerplate) allows you to pick your favorite test runner, and sets it up for you. If you want to use another test runner (like Mocha), install Vue CLI 3 and generate your own starter project. Then you can migrate the source files from my boilerplate right in it.

What should we test?

A common approach of unit testing is to only focus on the public API (aka black box testing). By overlooking implementation details, you’re allowing internals to change without having to adapt tests. After all, what you want to do is make sure your public API won’t break. What happens under the hood is indirectly tested, but all that matters is for the public API to remain reliable.

This is also the official recommendation from the Vue Test Utils guides. Therefore, we’ll only test what we can access from the outside of the component:

  • user interactions
  • props changes

We won’t directly test computed properties, methods, or hooks. These will be implicitly tested by testing the public interface.

Setting up a spec file

Like with regular tests, each component has a spec file which describes all tests we want to run.

Specs are JavaScript files. By convention, they have the same name as the components they’re testing, plus a .spec suffix.

Go ahead and create a test/unit/Rating.spec.js file:

// Rating.spec.js
import { shallowMount } from '@vue/test-utils'import Rating from '@/components/Rating'
describe('Rating', () => { // your tests go here})

We’ve imported our Rating component and shallowMount. The latter is a Vue Test Utils function which lets us mount our component without mounting its children.

The describe function call wraps all the test we’re about to write — it describes our testing suite. It has its own scope, and can itself wrap other nested suites.

Enough said, let’s start writing tests.

Identifying testing scenarios

When we look at Rating from the outside, we can see it does the following:

  • it renders a list of stars which is equal to the value of the maxStars prop the user passes
  • it adds an active class to each star whose index is lower than or equal to the stars prop the user passes
  • it toggles the active class on a star when the user clicks it and removes it on the next stars
  • it toggles the icons star and star-o when the user clicks a star
  • it renders a counter if the user sets the hasCounter prop to true, hides it if they set it to false, and displays text saying how many stars of the maximum number of stars are currently active.

Notice we’re only looking at what the component does from the outside. We don’t care that clicking a star executes the rate method, or that the internal stars data property changes. We could rename these, but this shouldn’t break our tests.

Our first test

Let’s write our first test. We first need to manually mount our component with shallowMount, and store it in a variable on which we’ll perform assertions. We can also pass props through the propsData attribute, as an object.

The mounted component is an object which comes with a handful of useful utility methods:

describe('Rating', () => { const wrapper = shallowMount(Rating, { propsData: { maxStars: 6, grade: 3 } }) it('renders a list of stars with class `active` equal to prop.grade', () => { // our assertion goes here })})

Then, we can write our first assertion:

it('renders a list of stars with class `active` equal to prop.grade', () => { expect(wrapper.findAll('.active').length).toEqual(3)})

Let’s analyze what’s happening here. First, we’re using Jest’s expect function, which takes the value we want to test as an argument. In our case, we call the findAll method on our wrapper to fetch all elements with an active class. This returns a WrapperArray, which is an object that contains an array of Wrappers.

A WrapperArray has two properties: wrappers (the contained Wrappers) and length (the number of Wrappers). The latter is what we need to have the expected number of stars.

The expect function also returns an object on which we can call methods to test the passed value. These methods are called matchers. Here, we use the toEqual matcher and pass it the expected value as in arguments. The method returns a boolean, which is what a test expects to either pass or fail.

To summarize, here we say we expect the total amount of elements with the class active we find in our wrapper to be equal to 3 (the value we assigned to the grade prop).

In your terminal, run your test:

npm run test:unit

You should see it pass ?

Time to write some more.

Simulating user input

Vue Test Utils makes it easy to simulate what real users end up doing in production. In our case, users can click on stars to toggle them. We can fake this in our tests with the trigger method, and dispatch all kinds of events.

it('adds `active` class on an inactive star when the user clicks it', () => { const fourthStar = wrapper.findAll('.star').at(3) fourthStar.trigger('click') expect(fourthStar.classes()).toContain('active')})

Here, we first get our fourth star with findAll and at, which returns a Wrapper from a WrapperArray at the passed index (zero-based numbering). Then, we simulate the click event on it — we’re mimicking the action from a user who would click or tap the fourth star.

Since we set the grade prop to 3, the fourth star should be inactive before we click, therefore the click event should make it active. In our code, this is represented by a class active which we append on stars only when they’re activated. We test it by calling the classes method on the star, which returns its class names as an array of strings. Then, we use the toContain matcher to make sure the active class is here.

Setup and teardown

Since we’ve triggered a click on our component, we’ve mutated its state. The problem is that we’re using that same component for all our tests. What happens if we change the order of our tests, and move this one to first position? Then the second test would fail.

You don’t want to rely on brittle things like order when it comes to tests. A test suite should be robust, and existing tests should ideally not change unless you’re breaking the API.

We want to make sure we always have a predictable wrapper to perform assertions on. We can achieve this with setup and teardown functions. These are helpers which let us initialize things before we run tests, and clean up afterward.

In our case, a way of doing it could be to create our wrapper before each test and destroy it afterwards.

let wrapper = null
beforeEach(() => { wrapper = shallowMount(Rating, { propsData: { maxStars: 6, grade: 3 } })})
afterEach(() => { wrapper.destroy()})
describe('Rating', () => { // we remove the `const wrapper = …` expression // …}

As their names suggest, beforeEach and afterEach run before and after each test, respectively. This way we can be 100% sure we’re using a fresh wrapper whenever we run a new test.

Special identifiers for tests

It’s never a good idea to mix selectors for styling and other purposes, such as test hooks.

What if you change the tag name or the class?

What if you don’t have a specific identifier on an element you want to test such as, in our case, the counter?

You don’t want to pollute your production code with classes which would be useless there. It would be much better to have dedicated hooks for tests, such as a dedicated data attribute, but only during tests. This way a mess isn’t left in the final build.

One way to handle this is to create a custom Vue directive.

The Vue instance has a directive method which takes two arguments — a name, and an object of functions for each hook of the component lifecycle when injected in the DOM. You can also pass a single function if you don’t care about a specific hook.

Let’s create a new directory called directives in src/, and add a test.js file. We’ll export the function we want to pass in our directive.

// test.js
export default (el, binding) => { // do stuff}

A directive hook can take several arguments and, in our case, we only need the first two: el and binding. The el argument refers to the element the directive is bound to. The binding argument is an object which contains the data we passed in the directive. This way we can manipulate the element as we like.

export default (el, binding) => { Object.keys(binding.value).forEach(value => { el.setAttribute(`data-test-${value}`, binding.value[value]) })}

We’re passing an object to our directive, so we can generate data attributes starting with data-test-. In the handler function, we iterate over each property of binding, and we set a data attribute — based on the name and value — on our element.

Now we need to register our directive so we can use it. We can do it globally but, in our case, we’re only going to register it locally — right in our Rating.vue component.

<script>import Test from '@/directives/test.js'
export default { // … directives: { Test }, // …}</script>

Our directive is now accessible under the v-test name. Try setting the following directive on the counter:

<span v-test="{ id: 'counter' }" v-if="hasCounter"> {{ stars }} of {{ maxStars }}</span>

Now inspect the HTML in your browser with the developer tools. Your counter should look like this:

<span data-test-id="counter">2 of 5</span>

Great, it works! Now, we don’t need this either in dev mode nor when we build the project. The sole purpose of this data attribute is to be able to target elements during tests, so we only want to set it up when we run them. For this, we can use the NODE_ENV environment variable provided by Webpack, the module bundler powering our project.

When we run tests, NODE_ENV is set to 'test'. Therefore, we can use it to determine when to set the test attributes or not.

export default (el, binding) => { if (process.env.NODE_ENV === 'test') { Object.keys(binding.value).forEach(value => { el.setAttribute(`data-test-${value}`, binding.value[value]) }) }}

Refresh your app in the browser and inspect the counter again: the data attribute is gone.

Now we can use the v-test directive for all elements we need to target. Let’s take our test from earlier:

it('adds `active` class on an inactive star when the user clicks it', () => { const fourthStar = wrapper.findAll('[data-test-id="star"]').at(3) fourthStar.trigger('click') expect(fourthStar.classes()).toContain('active')})

We’ve replaced the .star selector with [data-test-id="star"], which allows us to change classes for presentation purposes without breaking tests. We get one of the benefits of the single-responsibility principle and loose coupling — when your abstractions only have a single reason to change, you avoid all kinds of pesky side-effects.

Should we also use these hooks for the classes we test?

After setting this directive to target elements to test, you may be wondering if you should also use them to replace the classes we actively look for. Let’s look at the assertion from our first test:

expect(wrapper.findAll('.active').length).toEqual(3)

Should we use v-test on the elements with the active class, and replace the selector in the assertion? Great question.

Unit tests are all about testing one thing at a time. The first argument of the it function is a string, with which we describe what we’re doing from a consumer perspective.

The test that wraps our assertion says renders a list of stars with class active equal to prop.grade. This is what the consumer expects. When they pass a number to the grade property, they expect to retrieve an equal number of active or selected stars. Yet, in our component’s logic, the active class is precisely what we use to define this trait. We assign it depending on a specific condition, so we can visually differentiate active stars from the others. Here, the presence of this specific class is exactly what we want to test.

So, when deciding whether you should use a selector you already have or set a v-test directive, ask yourself the question: what am I testing, and does using this selector makes sense for a business logic perspective?

How is it different from functional or end-to-end tests?

At first, it might look odd to unit test components. Why would you unit test UI and user interactions? Isn’t that what functional tests are here for?

There is a fundamental yet subtle difference to make between testing a component’s public API — aka from a consumer perspective — and testing a component from a user perspective. First, let’s underline something important: we’re testing well-defined JavaScript functions, not pieces of UI.

When you look at a single-file component it’s easy to forget the component compiles into a JavaScript function. We’re not testing the underlying Vue mechanism which, from this function, causes UI-oriented side-effects like injecting HTML in the DOM. That’s what Vue’s own tests already take care of. In our case, our component is no different from any other function: it accepts input and returns an output. These causes and consequences are what we’re testing, and nothing else.

What’s confusing is that our tests look a bit different from regular unit tests. Usually, we write things like:

expect(add(3)(4)).toEqual(7)

There’s no debate here. Input and output of data, that’s all we care about. With components, we’re expecting things to render visually. We’re traversing a virtual DOM and testing for the presence of nodes. That’s also what you do with functional or end-to-end tests, with tools like Selenium or Cypress.io. So how does that differ?

You need not to confuse what we’re doing to fetch the data we want to test and the actual purpose of the test. With unit tests, we’re testing isolated behaviors. With functional or end-to-end tests, we’re testing scenarios.

A unit test makes sure a unit of the program behaves as expected. It’s addressed to the consumer of the component — the programmer who uses the component in their software. A functional test ensures a feature or a workflow behaves as expected, from a user perspective — the final user, who consumes the full software.

Going further

I won’t go into the detail of each test, because they all share a similar structure. You can find the full spec file on GitHub, and I strongly recommend you try to implement them yourself first. Software testing is an art as much as it is a science, and requires twice as much practice as it requires theory.

Don’t worry if you didn’t get everything, or if you struggle with writing your first tests: testing is notoriously hard. Also, if you have a question, don’t hesitate to hit me up on Twitter!

Originally published at frontstuff.io.

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

If this article was helpful, .

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started

ADVERTISEMENT

How to unit test your first Vue.js component (2024)

FAQs

What is the unit test function in Vue? ›

Unit tests execute the smallest units of code in isolation, in order to increase ease of adding new features and track down bugs. Vue's single-file components make it straight forward to write unit tests for components in isolation.

How to run a test case in Vue? ›

Here is how you can test Vue Js components using Jest Vue Test Utils.
  1. Step 1: Creating Vue Component.
  2. Step 2: Create Test File, Import Vue Component & Test Functionality from Test Utils.
  3. Step 3: Adding Test Cases in the Test File.
  4. Step 4: Run the Test Case.
  5. Step 5: Test different Test Cases of Vue Component.
Dec 29, 2023

Where do I put test files in Vue? ›

test. js in your project. You can place all test files in a test directory in the project root or in test directories next to your source files. Vitest will automatically search for them using the naming convention.

What are the steps to do component testing? ›

Component Testing Process
  1. Step1: Requirement Analysis.
  2. Step2: Test Planning.
  3. Step3: Test Specification.
  4. Step4: Test Implementation.
  5. Step5: Test Verification.
  6. Step6: Completion.
  7. Step7: Test Automation.

How do you run a unit test? ›

Run unit tests
  1. Open Test Explorer. To open Test Explorer, choose Test > Test Explorer from the top menu bar (or press Ctrl + E, T).
  2. Run your unit tests by clicking Run All (or press Ctrl + R, V). After the tests have completed, a green check mark indicates that a test passed. A red "x" icon indicates that a test failed.
Jan 19, 2024

How to run unit tests in JS? ›

Quick Tutorial #2: JavaScript Unit Testing Tutorial with Jest
  1. Step 1: Set Up the Environment. ...
  2. Step 2: Create Sample File. ...
  3. Step 3: Create a Test File. ...
  4. Step 4: Try a Test on the Example Function. ...
  5. Step 5: Run the Test. ...
  6. Step 6: Use a Loop to Test Multiple Values.
Jul 5, 2022

How do you do unit testing? ›

A unit test comprises three stages: planning, writing test cases and performing the unit test itself. In the first step, developers or QA professionals prepare and review the unit test. In the next step, they write test cases and scripts. In the third step the code is tested.

What is a vue test? ›

Pearson VUE is an international testing company that provides exams for a variety of purposes. Pearson VUE is an established and reliable international testing company that allows individuals to take tests related to a variety of different professions, such as medical certifications or teacher certifications.

How do you automate test cases? ›

How to Switch From Manual to Automation Testing?
  1. Step 1: Find Suitable Test Cases to Automate. ...
  2. Step 2: Find the Compatible Tools or Frameworks. ...
  3. Step 3: Manage the Tool Development Process. ...
  4. Step 4: Generate-Execute-Report-Maintain. ...
  5. Step 5: Monitor the Automation Tool's Effectiveness.

How can I create test cases? ›

How to write test cases: A step-by-step guide
  1. Define the area you want to cover from the test scenario.
  2. Ensure the test case is easy for testers to understand and execute.
  3. Understand and apply relevant test designs.
  4. Use a unique test case ID.
  5. Use the requirements traceability matrix in testing for visibility.
Mar 29, 2024

Where do you put unit test files? ›

Make your tests easy to find.
  • Put your unit tests in a test project named after the project they test. ...
  • Put your unit tests separated in files named after the unit of work or entry point of the code you're testing. ...
  • Give enough details to your readers, but not too many to make your tests noisy.
Dec 6, 2021

Is Jest better than Mocha? ›

Overall, Mocha tends to run faster and support speedier testing than Jest. That said, the configuration of tests plays into the speed of performance, so Mocha is not necessarily faster than Jest every time. If speed is your priority, Mocha is nevertheless the way to go.

How do I add test cases to test run? ›

To add a test case, open the relevant test run and click the in the tracker toolbar. Test Cases can be added to the test run by dragging and dropping them from the test case tree in right pane to the Test Case section of the current test run. The order of the newly added test case can also be modified.

How do you test a component class? ›

Component class testing should be kept very clean and simple. It should test only a single unit. At first glance, you should be able to understand what the test is testing. Consider this LightswitchComponent which toggles a light on and off (represented by an on-screen message) when the user clicks the button.

How do you check if an object is a component in Vue? ›

The simplest way to check if a an object is a Vue component, as in 2020l, is probably the _isVue property of the component, which, in case of the given object being a Vue. js component, exists as key-value at the root of the object and returns true.

How do I run a component test in Intellij? ›

Run tests directly in a file or folder
  1. Place the caret at the test class to run all tests in that class, or at the test method, and press Ctrl Shift F10 . ...
  2. To run all tests in a folder, select this folder in the Project tool window and press Ctrl Shift F10 or select Run Tests in 'folder' from the context menu.
Jul 11, 2024

How do I test my React components? ›

There are a few ways to test React components. Broadly, they divide into two categories: Rendering component trees in a simplified test environment and asserting on their output. Running a complete app in a realistic browser environment (also known as “end-to-end” tests).

Top Articles
Latest Posts
Recommended Articles
Article information

Author: Roderick King

Last Updated:

Views: 6461

Rating: 4 / 5 (71 voted)

Reviews: 86% of readers found this page helpful

Author information

Name: Roderick King

Birthday: 1997-10-09

Address: 3782 Madge Knoll, East Dudley, MA 63913

Phone: +2521695290067

Job: Customer Sales Coordinator

Hobby: Gunsmithing, Embroidery, Parkour, Kitesurfing, Rock climbing, Sand art, Beekeeping

Introduction: My name is Roderick King, I am a cute, splendid, excited, perfect, gentle, funny, vivacious person who loves writing and wants to share my knowledge and understanding with you.