Back Arrow
From the blog

How I Started Writing Unit Tests for Vue Components - Part 2

So, it's been a year since the last article, and a lot has changed. In this one, we're going to talk about integrating with Mock Service Worker (MSW).

Dmitry Simonov

Frontend Developer

So, it's been a year since the last article, and a lot has changed. In this one, we're going to talk about integrating with Mock Service Worker (MSW). I'll also describe what I tried to implement in my quest for system resilience - what worked out and what didn't.

So, did my tests actually help me?

I can't say the time investment paid off in spades, but one thing's for sure - it definitely wasn't a waste of time.

Here are the main areas where the tests really proved their worth:

  • When contracts were lost or changed;
  • Fixing the fallout from merge conflicts (given the quirks of our processes, this is the most common scenario);
  • Refactoring (it's hard to be objective here since our project's test coverage isn't huge, but before any refactoring, I try to at least cover the code with local tests).

Then again, all those fancy things like Cursor with their powerful autocomplete freed up some time, so why not spend a bit of it on tests?

The Fight for Reliability, or Reality Strikes Back

Let me start with what didn't work out.

The first thing I tried was implementing E2E tests with Playwright. You know, testing business logic in the browser by simulating real user actions.

In our existing project, this turned out to be really tough. The main problem was setting up the initial test data. In my case, that meant the database. It needed to be as small as possible, but still have all the necessary data for testing.

In theory, it sounds simple: take a database, tweak the data, create a Docker image, and boom - you're golden. Well, I got stuck at the very first step of preparing that database. It requires a firm decision and a coordinated effort, meaning help from the backend team and DevOps (who are always busy). In the end, on my project, we shelved the idea for the time being.

I also tried replacing actual backend interaction by mocking API requests directly within Playwright, but that felt like a dead end. Maintaining yet another set of mocks (on top of the MSW we already had) combined with the slow browser startup times just didn't seem rational, unless for some very specific tasks.

About Unit Tests and MSW

All in all, I decided to focus on unit tests (which, in the classic sense, are more like integration tests in our context). They're fast, isolated, simple, and reliable.

To mock network interactions, I set up MSW (Mock Service Worker). This later allowed us to practice contract-first programming and parallel development.

So, first you install MSW (the official guide is your best friend here).

Then, I moved the vite configuration for it into vitest.workspace.js (note: in new versions DEPRECATED). This isn't mandatory, but it's convenient if you need to separate node and browser environments.

import { defineWorkspace } from 'vitest/config';

export default defineWorkspace([
  'packages/*',
  {
    extends: './vite.config.js',
    test: {
      environment: 'jsdom',
      name: 'unit',
      include: ['src/**/*.spec.{ts,js}'],
      deps: {
        inline: ['element-plus'],
      },
      setupFiles: ['./src/mocks/setup.ts'], // path for the msw config
    },
  },
]);

Since it's an independent service, I put it in a mocks folder, so it's easy to cleanly remove if needed.

The structure
import { server } from './server.ts';
import { afterAll, afterEach, beforeAll } from 'vitest';

beforeAll(() =>  return server.listen({ onUnhandledRequest: 'warn' });
afterAll(() => server.close());
afterEach(() => server.resetHandlers());

user/handlers.ts

import { GET_USERS } from '@/api/constants/APIEndPoints.js';
import { HttpResponse, http } from 'msw';
import { USERS_FAKE_RESPONSE} from './fixtures.ts';

export const handlers = [
  http.get('*' + GET_USERS , () => {
    return HttpResponse.json(USERS_FAKE_RESPONSE);
  }),

];

Now, whenever a call is made to the URL defined in the GET_USER identifier, it will return the value stored in USER_FAKE_RESPONSE.

Interestingly, MSW, especially with its plugins, can generate handlers from an openApi.json file, which can cover all your API requests. It can also use faker.js to generate response data with fake values.

I'm not a big fan of that approach myself (it can complicate parallel work), so I prefer to create response fixtures and handlers manually, and then fill them in - even using AI helpers sometimes - which results in more human-readable responses.

export const USER_FAKE_RESPONSE = {
  items:[
    { firstName: 'John' , lastName: 'Smith'}
    { firstName: 'Willy' , lastName: 'Willson'}
  ]
}

Using it in Tests

For a clear example, let's imagine we have a component with a button to fetch users and a block to display the response. A traditional test might look something like this (a detailed test was in the previous article; this is just a schematic).

import * as USER_API from 'some api folder'
let wrapper

const createComponent = (params {}) => {
  wrapper = shallowMount(OurGetUsersComponent, {
    props: {
      ...params.props,
    },
    global: {
      renderStubDefaultSlot: true,
      stubs: {
        ...params.stubs,
      },
    },
  });
};

test('Handling user retrieval when the Find button is clicked', async () => {
const spyGetUsers =  vi.spyOn(USER_API, 'getUsersRequest').mockImplementation(() =>{  items:[
    { firstName: 'John' , lastName: 'Smith'}
    { firstName: 'Willy' , lastName: 'Willson'}
  ]}) 

  createComponent ()
  const buttonNode = wrapper.find('.button') //not a very good selector, but we only have 1 button
  await buttonNode.trigger('click');

  await flushPromises();
  expect(spyGetUsers).toHaveBeenCalled(); //here you can also check the parameters


  expect(wrapper.text()).toContain('Smith')
  expect(wrapper.text()).toContain('Willson')
 
});

That approach works, but what if we need to test the behavior when the server returns an error? For example, when a 500 error triggers a toast notification saying, "The server is temporarily unavailable, please try again later."

This is exactly where MSW comes to the rescue.

import { server } from '@/mocks/server';
import { http, HttpResponse } from 'msw';
import { USER_FAKE_RESPONSE } from '...fixtures'
import * as MESSAGE_MODULE from "utils"
import { GET_USERS } from '@/api/constants/APIEndPoints.js';

let wrapper

const createComponent = (params {}) => {
  wrapper = shallowMount(OurGetUsersComponent, {
    props: {
      ...params.props,
    },
    global: {
      renderStubDefaultSlot: true,
      stubs: {
        ...params.stubs,
      },
    },
  });
};

test('Handling user retrieval when the search button is clicked', async () => {
const spyGetUsers =  vi.spyOn(USER_API, 'getUsersRequest') // the implementation already exists in MSW and doesn't need to be duplicated here

  createComponent ()
  // it's better to search the same way as the user  
  const buttonNode = wrapper.findAll('.button').filter(item=>item.text()=="Search")[0] 
  await buttonNode.trigger('click');

  await flushPromises();
  expect(spyGetUsers).toHaveBeenCalled(); // This step might be redundant since the result is what matters to the user

  expect(wrapper.text()).toContain(USER_FAKE_RESPONSE.items[0].lastName)
  expect(wrapper.text()).toContain(USER_FAKE_RESPONSE.items[1].lastName)
 
});

test('Handling server errors when retrieving users', async ()=>{
spyMessage = vi.spyOn(MESSAGE_MODULE , 'showErrorMessage')

  server.use(
    http.get('*' + GET_USERS, () => {
      return new HttpResponse(null, { status: 500 });
    }),
  );
  createComponent ()
  const buttonNode = wrapper.findAll('.button').filter(item=>item.text()=="Search")[0] 
  await buttonNode.trigger('click');

expect(spyMessage ).toHaveBeenCalledWith({message: 'The server is temporarily unavailable, please try again later' });

})

This way, you can make your unit tests a little more honest and your team's capabilities a little broader.

Conclusion

It's easy to start working with us. Just fill the brief or call us.

Find out more
White Arrow
From the blog
Related articles

Performance Issues in Web Services: A Practical Guide to Identification and Resolution

Dmitry Bastron

Performance is not a feature to add later, but a core requirement as vital as security. This guide provides a structured approach to building performance into your web services from the start, ensuring they are fast and scalable by design.

Development
WebDev

Setting SMART Goals for Digital Product Success

Andrey Stepanov

This quick guide helps you define clear objectives and track progress effectively—ensuring every milestone counts.

Development

Building Teams for Digital Products: Essential Roles, Methods, and Real-World Advice

Andrey Stepanov

A digital product isn’t just about features or design—it’s about teamwork. In this article, we break down the essential roles in digital product development.

Development

Goals in Digital Development: How to Launch a Digital Product Without Failure

Andrey Stepanov

Essential tips for creating a development team that works toward product goals, not just tasks.

Development

How to Become a Kentico MVP

Dmitry Bastron

Hi, my name is Dmitry Bastron, and I am the Head of Development at ByteMinds. Today, I’d like to share how I achieved Kentico MVP status, why I chose this CMS, and what it takes to follow the same path and earn the coveted MVP badge.

Kentico

How can a team lead figure out a new project when nothing is clear?

Maria Serebrovskaya

Hello! My name is Maria, and I am a team lead and backend developer at ByteMinds. In this article, I will share my experience: I’ll explain how to understand a complex project, establish processes, and make it feel like "your own".

Development

How I Started Writing Unit Tests for Vue Components

Dmitry Simonov

In this article, we’ll take a look at how you can start testing Vue components.

Vue
vuejs

Inspecting raw database data in Xperience by Kentico

Dmitry Bastron

This article is a cheat sheet to inspect what's going on with the imported data by Xperience by Kentico Migration Toolkit, resolve some CI/CD issues, and on many other occasions!

Kentico

Learnings from using Sitecore ADM

Anna Bastron

Let's try to understand how the ADM module works, its limitations, and tactics for optimising its performance.

Sitecore

Your last migration to Xperience by Kentico

Dmitry Bastron

The more mature Xperience by Kentico products become, the more often I hear "How can we migrate there?”

Kentico

5 Key Software Architecture Principles for Starting Your Next Project

Andrey Stepanov

In this article, we will touch on where to start designing the architecture and how to make sure that you don’t have to redo it during the process.

Architecture
Software development

Assessing Algorithm Complexity in C#: Memory and Time Examples

Anton Vorotyncev

Today, we will talk about assessing algorithm complexity and clearly demonstrate how this complexity affects the performance of the code.

.NET

Top 8 B2B Client Service Trends to Watch in 2024

Tatiana Golovacheva

The development market today feels like a race - each lap is quicker, and one wrong move can cost you. In this race, excellent client service can either add extra points or lead to a loss due to high competition.

Customer Service
Client Service

8 Non-Obvious Vulnerabilities in E-Commerce Projects Built with NextJS

Dmitry Bastron

Ensuring security during development is crucial, especially as online and e-commerce services become more complex. To mitigate risks, we train developers in web security basics and regularly perform third-party penetration testing before launch.

Next.js
Development

How personalisation works in Sitecore XM Cloud

Anna Bastron

In my previous article, I shared a comprehensive troubleshooting guide for Sitecore XM Cloud tracking and personalisation. This article visualises what happens behind the scenes when you enable personalisation and tracking in your Sitecore XM Cloud applications.

Sitecore

Server and client components in Next.js: when, how, and why?

Sergei Pestov

All the text and examples in this article refer to Next.js 13.4 and newer versions, in which React Server Components have gained stable status and become the recommended approach for developing applications using Next.js.

Next.js

How to properly measure code speed in .NET

Anton Vorotyncev

Imagine you have a solution to a problem or a task, and now you need to evaluate the optimality of this solution from a performance perspective.

.NET

Formalizing API Workflow in .NET Microservices

Artyom Chernenko

Let's talk about how to organize the interaction of microservices in a large, long-lived product, both synchronously and asynchronously.

.NET

Hidden Aspects of TypeScript and How to Resolve Them

Dmitry Berdnikov

We suggest using a special editor to immediately check each example while reading the article. This editor is convenient because you can switch the TypeScript version in it.

TypeScript

Troubleshooting tracking and personalisation in Sitecore XM Cloud

Anna Gevel

One of the first things I tested in Sitecore XM Cloud was embedded tracking and personalisation capabilities. It has been really interesting to see what is available out-of-the-box, how much flexibility XM Cloud offers to marketing teams, and what is required from developers to set it up.

Sitecore

Mastering advanced tracking with Kentico Xperience

Dmitry Bastron

We will take you on a journey through a real-life scenario of implementing advanced tracking and analytics using Kentico Xperience 13 DXP.

Kentico
Devtools

Why is Kentico of such significance to us?

Anastasia Medvedeva

Kentico stands as one of our principal development tools. We believe it would be fitting to address why we opt to work with Kentico and why we allocate substantial time to cultivating our experts in this DXP.

Kentico

Where to start learning Sitecore - An interview with Sitecore MVP Anna Gevel

Anna Gevel

As a software development company, we at Byteminds truly believe that learning and sharing knowledge is one of the best ways of growing technical expertise.

Sitecore

Sitecore replatforming and upgrades

Anastasia Medvedeva

Our expertise spans full-scale builds and support to upgrades and replatforming.

Sitecore

How we improved page load speed for a Next.js e-commerce website by 50%

Sergei Pestov

How to stop the decline of the performance indicators of your e-commerce website and perform optimise page load performance.

Next.js

Sitecore integration with Azure Active Directory B2C

Dmitry Bastron

We would like to share our experience of integrating Sitecore 9.3 with Azure AD B2C (Azure Active Directory Business to Consumer) user management system.

Sitecore
Azure

Dynamic URL routing with Kontent.ai

We'll consider the top-to-bottom approach for modeling content relationships, as it is more user-friendly for content editors working in the Kontent.ai admin interface.

Kontent Ai

Headless CMS. Identifying Ideal Use Cases and Speeding Up Time-to-Market

Andrey Stepanov

All you need to know about Headless CMS. We also share knowledge about the benefits of Headless CMS, its pros and cons.

Headless CMS

Enterprise projects: what does a developer need to know?

Fedor Kiselev

Let's talk about what enterprise development is, what nuances enterprise projects may have, and which skills you need to acquire to successfully work within the .NET stack.

Development

Fixed Price, Time & Materials, and Retainer: How to Choose the Right Agreement for Your Project with Us

Andrey Stepanov

We will explain how these agreements differ from one another and what projects they are suitable for.

Customer success

Sitecore Personalize: tips & tricks for decision models and programmable nodes

Anna Gevel

We've collected various findings around decision models and programmable nodes working with Sitecore Personalize.

Sitecore

Umbraco replatforming and upgrades

Anastasia Medvedeva

Our team boasts several developers experienced in working with Umbraco, specialising in development, upgrading, and replatforming from other CMSs to Umbraco.

Umbraco

Kentico replatforming and upgrades

Anastasia Medvedeva

Since 2015, we've been harnessing Kentico's capabilities well beyond its core CMS functions.

Kentico

Interesting features of devtools for QA

Egor Yaroslavcev

Chrome DevTools serves as a developer console, offering an array of in-browser tools for constructing and debugging websites and applications.

Devtools
QA

Activity logging with Xperience by Kentico

Dmitry Bastron

We'll dive into practical implementation in your Xperience by Kentico project. We'll guide you through setting up a custom activity type and show you how to log visitor activities effectively.

Kentico
This website uses cookies. View Privacy Policy.