Nina Diana

Nina Diana

1567482020

Knowing What To Test - Vue Component Unit Testing

While it’s possible to test either too much or too little, my observation is that developers will usually err on the side of testing too much. After all, no one wants to be the guy or girl who’s under-tested component crashed the app in production.

In this article, I’ll share with you some guidelines I use for unit testing components that ensure I don’t spend forever writing tests but provide enough coverage to keep me out of trouble.

I’ll assume you’ve already had an introduction to Jest and Vue Test Utils.

Example component

Before we get to the guidelines, let’s first get familiar with the following example component that we’ll be testing. It’s called Item.vue and is a product item in an eCommerce app.

Here’s the component’s code. Note there are three dependencies: Vuex ($store), Vue Router ($router) and Vue Auth ($auth).

Item.vue

<template>
  <div>
    <h2>{{ item.title }}</h2>
    <button @click="addToCart">Add To Cart</button>
    <img :src="item.image"/>
  </div>
</template>
<script>
export default {
  name: "Item",
  props: [ "id" ],
  computed: {
    item () {
      return this.$store.state.find(
        item => item.id === this.id
      );
    }
  },
  methods: {
    addToCart () {
      if (this.$auth.check()) {
        this.$store.commit("ADD_TO_CART", this.id);
      } else {
        this.$router.push({ name: "login" });
      }
    }
  }
};
</script>

Spec file setup

Here’s the spec file for the tests. In it, we’ll shallow mount our components with Vue Test Utils, so I’ve imported that, as well as the Item component we’re testing.

I’ve also created a factory function that will generate an overrideable config object, saving us having to specify props and mocking the three dependencies in each test.

item.spec.js

import { shallowMount } from "@vue/test-utils";
import Item from "@/components/Item";


function createConfig (overrides) {
  const id = 1;
  const mocks = {
    // Vue Auth
    $auth: {
      check: () => false
    },
    // Vue Router
    $router: {
      push: () => {}
    },
    // Vuex
    $store: {
      state: [ { id } ],
      commit: () => {}
    }
  };
  const propsData = { id };
  return Object.assign({ mocks, propsData }, overrides);
}


describe("Item.vue", () => {
  // Tests go here
});

Identify the business logic

The first and most important question to ask about a component you want to test is “what is the business logic?”, in other words, what is the component meant to do?

For Item.vue, here is the business logic:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Identify the inputs and outputs

When you unit test a component, you treat it as a black box. Internal logic in methods, computed properties etc, only matter insofar as they affect output.

So, the next important thing is to identify the inputs and outputs of the component, as these will also be the inputs and outputs of your tests.

In the case of Item.vue, the inputs are:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

While the outputs are:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Some components may also have forms and events as inputs, and emit events as outputs.#### Test 1: router called when guest clicks button

One piece of business logic is “If the user is a guest, clicking the Add to Cart button redirects them to the login page”. Let’s write a test for that.

We’ll set up the test by shallow mounting the component, then finding and clicking the Add to Cart button.

test("router called when guest clicks button", () => {
  const config = createConfig();
  const wrapper = shallowMount(Item, config);
  wrapper
    .find("button")
    .trigger("click");
  // Assertion goes here
}

We’ll add an assertion in a moment.

Don’t go beyond the boundaries of the input and output

It’d be tempting in this test to check that the route changed to that of the login page after clicking the button e.g.

import router from "router";


test("router called when guest clicks button", () => {
  ...
  // Wrong
  const route = router.find(route => route.name === "login");
  expect(wrapper.vm.$route.path).toBe(route.path);
}

While this does test the component output implicitly, it’s relying on the router to work, which should not be the concern of this component.

It’s better to directly test the output of this component, which is the call to $router.push. Whether the router completes that operation is beyond the scope of this particular test.

So let’s spy on the push method of the router, and assert that it gets called with the login route object.

import router from "router";


test("router called when guest clicks button", () => {
  ...
  jest.spyOn(config.mocks.$router, "push");
  const route = router.find(route => route.name === "login");
  expect(spy).toHaveBeenCalledWith(route);
}

Test 2: vuex called when auth user clicks button

Next, let’s test the business logic for “If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART”.

To re-iterate the above lesson, you don’t need to check if the Vuex state gets modified. We would have a separate test for the Vuex store to verify that.

This component’s job is simply to make the commit, so we just need to test it does it that.

So let’s first override the $auth.check mock so it returns true (as it would for a logged-in user). We’ll then spy on the commit method of the store, and assert it was called after the button is clicked.

test("vuex called when auth user clicks button", () => {
  const config = createConfig({
    mocks: {
      $auth: {
        check: () => true
      }
    }
  });
  const spy = jest.spyOn(config.mocks.$store, "commit");
  const wrapper = shallowMount(Item, config);
  wrapper
    .find("button")
    .trigger("click");
  expect(spy).toHaveBeenCalled();
}

Don’t test functionality of other libraries

The Item component displays a store item’s data, specifically the title and image. Maybe we should write a test to specifically check these? For example:

test("renders correctly", () => {
  const wrapper = shallowMount(Item, createConfig());
  // Wrong
  expect(wrapper.find("h2").text()).toBe(item.title);
}

This is another unnecessary test as it’s just testing Vue’s ability to take in data from Vuex and interpolate it in the template. The Vue library already has tests for that mechanism so you should rely on that.

Test 3: renders correctly

But hang on, what if someone accidentally renames title to name and then forgets to update the interpolation? Isn’t that something worth testing for?

Yes, but if you test every aspect of your templates like this, where do you stop?

The best way to test markup is to use a snapshot test to check the overall rendered output. This will cover not just the title interpolation, but also the image, the button text, any classes, etc.

test("renders correctly", () => {
  const wrapper = shallowMount(Item, createConfig());
  expect(wrapper).toMatchSnapshot();
});

Here are some examples of other things there is no need to test:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Etc.

Wrap up

I think those three relatively simple tests are sufficient for this component.

A good mindset to have when unit testing components is to assume a test is unnecessary until proven otherwise.

Here are the questions you can ask yourself:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Happy testing!

Recommended Reading

VuePress: Documentation Made Easy

Creating a To-Do List App with Vue.js & Laravel

Angular vs. React vs. Vue: A performance comparison

Hooks are coming to Vue.js version 3.0

How to Enable CORS in a Node.js Express App - CodesQuery

Making the Move from jQuery to Vue

Vue and You

Programming Vue.js Fullpage Scroll

Create a Laravel Vue Single Page App

While it’s possible to test either too much or too little, my observation is that developers will usually err on the side of testing too much. After all, no one wants to be the guy or girl who’s under-tested component crashed the app in production.

In this article, I’ll share with you some guidelines I use for unit testing components that ensure I don’t spend forever writing tests but provide enough coverage to keep me out of trouble.

I’ll assume you’ve already had an introduction to Jest and Vue Test Utils.

Example component

Before we get to the guidelines, let’s first get familiar with the following example component that we’ll be testing. It’s called Item.vue and is a product item in an eCommerce app.

Here’s the component’s code. Note there are three dependencies: Vuex ($store), Vue Router ($router) and Vue Auth ($auth).

Item.vue

<template>
  <div>
    <h2>{{ item.title }}</h2>
    <button @click="addToCart">Add To Cart</button>
    <img :src="item.image"/>
  </div>
</template>
<script>
export default {
  name: "Item",
  props: [ "id" ],
  computed: {
    item () {
      return this.$store.state.find(
        item => item.id === this.id
      );
    }
  },
  methods: {
    addToCart () {
      if (this.$auth.check()) {
        this.$store.commit("ADD_TO_CART", this.id);
      } else {
        this.$router.push({ name: "login" });
      }
    }
  }
};
</script>

Spec file setup

Here’s the spec file for the tests. In it, we’ll shallow mount our components with Vue Test Utils, so I’ve imported that, as well as the Item component we’re testing.

I’ve also created a factory function that will generate an overrideable config object, saving us having to specify props and mocking the three dependencies in each test.

item.spec.js

import { shallowMount } from "@vue/test-utils";
import Item from "@/components/Item";


function createConfig (overrides) {
  const id = 1;
  const mocks = {
    // Vue Auth
    $auth: {
      check: () => false
    },
    // Vue Router
    $router: {
      push: () => {}
    },
    // Vuex
    $store: {
      state: [ { id } ],
      commit: () => {}
    }
  };
  const propsData = { id };
  return Object.assign({ mocks, propsData }, overrides);
}


describe("Item.vue", () => {
  // Tests go here
});

Identify the business logic

The first and most important question to ask about a component you want to test is “what is the business logic?”, in other words, what is the component meant to do?

For Item.vue, here is the business logic:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Identify the inputs and outputs

When you unit test a component, you treat it as a black box. Internal logic in methods, computed properties etc, only matter insofar as they affect output.

So, the next important thing is to identify the inputs and outputs of the component, as these will also be the inputs and outputs of your tests.

In the case of Item.vue, the inputs are:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

While the outputs are:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Some components may also have forms and events as inputs, and emit events as outputs.#### Test 1: router called when guest clicks button

One piece of business logic is “If the user is a guest, clicking the Add to Cart button redirects them to the login page”. Let’s write a test for that.

We’ll set up the test by shallow mounting the component, then finding and clicking the Add to Cart button.

test("router called when guest clicks button", () => {
  const config = createConfig();
  const wrapper = shallowMount(Item, config);
  wrapper
    .find("button")
    .trigger("click");
  // Assertion goes here
}

We’ll add an assertion in a moment.

Don’t go beyond the boundaries of the input and output

It’d be tempting in this test to check that the route changed to that of the login page after clicking the button e.g.

import router from "router";


test("router called when guest clicks button", () => {
  ...
  // Wrong
  const route = router.find(route => route.name === "login");
  expect(wrapper.vm.$route.path).toBe(route.path);
}

While this does test the component output implicitly, it’s relying on the router to work, which should not be the concern of this component.

It’s better to directly test the output of this component, which is the call to $router.push. Whether the router completes that operation is beyond the scope of this particular test.

So let’s spy on the push method of the router, and assert that it gets called with the login route object.

import router from "router";


test("router called when guest clicks button", () => {
  ...
  jest.spyOn(config.mocks.$router, "push");
  const route = router.find(route => route.name === "login");
  expect(spy).toHaveBeenCalledWith(route);
}

Test 2: vuex called when auth user clicks button

Next, let’s test the business logic for “If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART”.

To re-iterate the above lesson, you don’t need to check if the Vuex state gets modified. We would have a separate test for the Vuex store to verify that.

This component’s job is simply to make the commit, so we just need to test it does it that.

So let’s first override the $auth.check mock so it returns true (as it would for a logged-in user). We’ll then spy on the commit method of the store, and assert it was called after the button is clicked.

test("vuex called when auth user clicks button", () => {
  const config = createConfig({
    mocks: {
      $auth: {
        check: () => true
      }
    }
  });
  const spy = jest.spyOn(config.mocks.$store, "commit");
  const wrapper = shallowMount(Item, config);
  wrapper
    .find("button")
    .trigger("click");
  expect(spy).toHaveBeenCalled();
}

Don’t test functionality of other libraries

The Item component displays a store item’s data, specifically the title and image. Maybe we should write a test to specifically check these? For example:

test("renders correctly", () => {
  const wrapper = shallowMount(Item, createConfig());
  // Wrong
  expect(wrapper.find("h2").text()).toBe(item.title);
}

This is another unnecessary test as it’s just testing Vue’s ability to take in data from Vuex and interpolate it in the template. The Vue library already has tests for that mechanism so you should rely on that.

Test 3: renders correctly

But hang on, what if someone accidentally renames title to name and then forgets to update the interpolation? Isn’t that something worth testing for?

Yes, but if you test every aspect of your templates like this, where do you stop?

The best way to test markup is to use a snapshot test to check the overall rendered output. This will cover not just the title interpolation, but also the image, the button text, any classes, etc.

test("renders correctly", () => {
  const wrapper = shallowMount(Item, createConfig());
  expect(wrapper).toMatchSnapshot();
});

Here are some examples of other things there is no need to test:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Etc.

Wrap up

I think those three relatively simple tests are sufficient for this component.

A good mindset to have when unit testing components is to assume a test is unnecessary until proven otherwise.

Here are the questions you can ask yourself:

  • It will display an item based on the id prop received
  • If the user is a guest, clicking the Add to Cart button redirects them to the login page
  • If the user is logged in, clicking the Add to Cart button will trigger a Vuex mutation ADD_TO_CART

Happy testing!

Recommended Reading

VuePress: Documentation Made Easy

Creating a To-Do List App with Vue.js & Laravel

Angular vs. React vs. Vue: A performance comparison

Hooks are coming to Vue.js version 3.0

How to Enable CORS in a Node.js Express App - CodesQuery

Making the Move from jQuery to Vue

Vue and You

Programming Vue.js Fullpage Scroll

Create a Laravel Vue Single Page App

#vue-js #javascript

What is GEEK

Buddha Community

Knowing What To Test - Vue Component Unit Testing

Software Testing 101: Regression Tests, Unit Tests, Integration Tests

Automation and segregation can help you build better software
If you write automated tests and deliver them to the customer, he can make sure the software is working properly. And, at the end of the day, he paid for it.

Ok. We can segregate or separate the tests according to some criteria. For example, “white box” tests are used to measure the internal quality of the software, in addition to the expected results. They are very useful to know the percentage of lines of code executed, the cyclomatic complexity and several other software metrics. Unit tests are white box tests.

#testing #software testing #regression tests #unit tests #integration tests

Tamia  Walter

Tamia Walter

1596754901

Testing Microservices Applications

The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.

This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?

A Brave New World

Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.

Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.

Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.

Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.

The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.

Keep in mind that there are additional things to focus on when testing microservices.

  • Microservices rely on network communications to talk to each other, so network reliability and requirements must be part of the testing.
  • Automation and infrastructure elements are now added as codes, and you have to make sure that they also run properly when microservices are pushed through the pipeline
  • While containerization is universal, you still have to pay attention to specific dependencies and create a testing strategy that allows for those dependencies to be included

Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.

Contract Testing as an Approach

As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.

Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.

Ways to Test Microservices

It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.

There are several ways to test microservices too, including:

  • Unit testing: Which allows developers to test microservices in a granular way. It doesn’t limit testing to individual microservices, but rather allows developers to take a more granular approach such as testing individual features or runtimes.
  • Integration testing: Which handles the testing of microservices in an interactive way. Microservices still need to work with each other when they are deployed, and integration testing is a key process in making sure that they do.
  • End-to-end testing: Which⁠—as the name suggests⁠—tests microservices as a complete app. This type of testing enables the testing of features, UI, communications, and other components that construct the app.

What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.

#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr

A Secret Weapon for Vue Unit Tests

If your Vue single-file components have dependencies, you’ll need to handle the dependencies somehow when you unit test the component.

One approach is to install the dependencies in the test environment, but this may overcomplicate your tests.

In this article, I’ll show you how to mock a module file in Jest by replacing it in your component’s graph of dependencies.

#vue.js #jest #testing #unit testing #vue

Luna  Mosciski

Luna Mosciski

1600583123

8 Popular Websites That Use The Vue.JS Framework

In this article, we are going to list out the most popular websites using Vue JS as their frontend framework.

Vue JS is one of those elite progressive JavaScript frameworks that has huge demand in the web development industry. Many popular websites are developed using Vue in their frontend development because of its imperative features.

This framework was created by Evan You and still it is maintained by his private team members. Vue is of course an open-source framework which is based on MVVM concept (Model-view view-Model) and used extensively in building sublime user-interfaces and also considered a prime choice for developing single-page heavy applications.

Released in February 2014, Vue JS has gained 64,828 stars on Github, making it very popular in recent times.

Evan used Angular JS on many operations while working for Google and integrated many features in Vue to cover the flaws of Angular.

“I figured, what if I could just extract the part that I really liked about Angular and build something really lightweight." - Evan You

#vuejs #vue #vue-with-laravel #vue-top-story #vue-3 #build-vue-frontend #vue-in-laravel #vue.js

Testing a Component in Vue.js

Before moving forward, we have some duplicated code in our spec. Backed up by the last commit on the green state we can try out some refactors. I can see 3 methods to extract to helper functions:

  1. Post object creation
  2. Wrapper creation
  3. Formatted date assertion

I will let you do this refactor but you can find my code.
After removing code duplications and making assertions more clear, let’s look back at our requirements for the post item.

#vue-test-utils #unit-testing #testing