preface

Unit testing refers to the examination and verification of the smallest testable units in software. For unit test in unit of meaning, in general, according to the actual situation to determine the specific meaning, such as unit refers to a function in C language, Java unit refers to a class, graphical software can refer to a window or a menu, the vue, react, and presents the frame of the front end, the most important thing is that in view of the component unit tests

Why introduce unit tests?

Present age, a variety of programming language and development framework, thriving integration tools, software engineers, however, are still struggling in the first line, is a bug, legacy code, technical debt, refactoring with, when your project is large enough, in the process of adding modules and components, is is likely to affect the module before. But the affected modules had already been tested, and few testers would retest the system as we iterated. Therefore, the affected module may have an invisible bug deployed online. So we use automated testing. The most important function is to ensure the correct operation of the whole system and the robustness of the system in each iteration of large projects. The following points are summarized:

  • Automated testing saves time
  • Reduce low-level bugs
  • Documentation describing the behavior of components is provided
  • Ability to improve code while writing single tests
  • Easy to read components and facilitate refactoring
  • Prove that your work is done
  • Facilitate code review
  • The code performance
  • Provide some metrics

Unit Test Overview

Unit tests are usually for the smallest part of the application, and in VUE components are the units to be tested (described below)

Let’s start with a simple unit test that uses the sum function to calculate the sum of two numbers in the code.

A unit test is a function that calls a function alone in the source code and asserts that it behaves correctly. Consider the following example, a relatively simple program that exports a sum function and then runs the function, asserting that it will throw an error if it does not return.

// sum.js
export default function sum(a,b){
    return a + b
}
// sum.spec.js
import sum from './sum.js'
function testSum(){
    if(sum(1.2)! = =3) {throw new Error('the sum (1, 2) not return 3')
    }
}
testSum()
Copy the code

Because unit tests are conducted against isolated units, good unit tests, when written, accurately expose code problems.

Also in testing, we might focus on the snapshot test, snapshot test found that is similar to the difference snapshot test will run the program compare screenshots, if there are differences, errors will occur, in vue testing, vueTestUtil provides similar ability, you can compare the js serializable value, in the component is to compare the dom output

Test development patterns

If you’re interested in Test Development, you’ve probably heard of DDD-Test Driven Development and DDD-Behavior Driven Development.

1. Test Driven Development

Test-driven Development, English full name test-driven Development, referred to as TDD, is a new Development method different from the traditional software Development process. It requires that you write test code before you write the code for a feature, and then only write the code for the feature that makes the test pass, and the test drives the development. This helps write clean, usable and high-quality code and speeds up the development process

First of all, the developer before writing business logic, to write some test cases If you run these test cases, will be the result of the failure, because we don’t realize to test the business logic implementation of the business logic to run test cases, check pass, if you are a good developer, may these cases can be through the repair of test cases, Or refactoring

When we develop new features, we repeat the above steps, the core of which is the pre-test case. The flow chart is as follows:

For example: let’s describe TDD with a concrete example. Suppose our requirement is to implement a factorial function. Let’s use JEST to implement this test case

var fac = require(".. /src/index.js");

test("If I input a negative number, I should return NaN.".() = > {
  expect(fac(-1)).toBe(NaN);
});

test("Enter 0, should return 1.".() = > {
  expect(fac(0)).toBe(1);
});

test("Enter 1, should return 1.".() = > {
  expect(fac(1)).toBe(1);
});

test("Enter 2, should return 2.".() = > {
  expect(fac(2)).toBe(2);
});

test("If you enter 3, you should return 6.".() = > {
  expect(fac(3)).toBe(6);
});
Copy the code

Running this test case is bound to fail because we haven’t implemented the FAC function yet, so let’s implement the factorial function

module.exports = function fac(n) {
  if (n < 0) return NaN;
  if (n === 0) return 1;
  return n * fac(n - 1);
};
Copy the code

Now we run the test case again and get the following result:

As you can see, all the cases have passed, and this is the DEVELOPMENT mode of TDD

2. BDD – Behavior Driven Development

In traditional software development, business people to get the demand, the demand to the demand analysis, demand analysis personnel writing requirements specifications or design, and software developers, according to the specifications for architecture design and code development, then the tester according to specifications. Write test cases to test, from requirements to test delivery, there are a number of different roles, During this period, it is easy to have information loss and misunderstanding, and it is difficult for the R&D team to deliver qualified products as long as there is only one link error.

BDD is an agile software development technique that encourages collaboration between developers, QA, and non-technical or business participants in software and is particularly suitable for agile projects

Here’s an example:

var fac = require(".. /src/index.js");

describe("Verify factorial function faC:".function () {
  it("If I input a negative number, I should return NaN.".() = > {
    expect(fac(-1)).toBe(NaN);
  });

  it("Enter 0, should return 1.".() = > {
    expect(fac(0)).toBe(1);
  });

  it("Enter 1, should return 1.".() = > {
    expect(fac(1)).toBe(1);
  });

  it("Enter 2, should return 2.".() = > {
    expect(fac(2)).toBe(2);
  });

  it("If you enter 3, you should return 6.".() = > {
    expect(fac(3)).toBe(6);
  });
});
Copy the code

Run the test case and get the result:

Content of the code and test results, and comparison the difference is not large, the main difference is the difference between the language, test cases of BDD looks just like looking at a document, the structure is clear, for the team, code reading, promote restructuring has to be reckoned with, when you can fluent when reading the test cases, nature also can write better code.

The examples here only describe the differences from test-driven development and do not represent true behavior-driven development, which is more of a conceptual theory

Conclusion: BDD focuses more on functions than results. To borrow another saying in the industry, BDD helps developers design software, while TDD helps developers test software.

Unit tests in Vue

Unit testing allows you to test separate units of code in isolation. The purpose is to provide developers with confidence in the code. By writing thoughtful and meaningful tests, you can be confident that you can keep your application functional and stable while building new features or refactoring existing code. Unit testing for a Vue application is not significantly different from testing for any other type of application.

Framework to choose

If you are a Vue developer, you should be familiar with the template syntax of Vue components. Template, style, script template syntax is more direct and natural than React Jsx syntax. Vue uses components as minimum test units.

Although unit tests are usually not directly related to the framework, you need to evaluate them for the set of features, performance and support for precompiling single-file components, value generated by single tests, and ease of development.

First-class error reporting

It is critical for a unit testing framework to provide useful error information when a test fails. This is the job of the assertion library. An assertion with high-quality error information minimizes the time required to debug the problem. In addition to simply telling you what test failed, the assertion library should also provide additional context and the reason for the test failure, such as expected results vs. The actual result. Some unit testing frameworks, such as Jest, include assertion libraries. Others, such as Mocha, require you to install assertion libraries separately (usually Chai).

Active community and team

Because the leading unit testing frameworks are open source, having an active community is critical for teams that want to maintain their tests over the long term and ensure that the project itself remains active. As an added bonus, an active community will have more support for you at any time you encounter problems.

Here we consider using the Jest framework, a JavaScript testing framework that focuses on simplicity. One unique feature is the ability to generate snapshots for tests to provide another way to validate application units.

Jest is the most full-featured test runner. It requires minimal configuration, has JSDOM installed by default, built-in assertions, and a very good command-line user experience.

Jest data

Jest’s official website

Vue CLI official plug-in – Jest

Vue provides a handy library of testing tools: Vue Test Utils. We’ll show you how to use Vue Test Utils to unit Test Vue components.

Vue Test Utils

It provides a rich API with powerful features such as rendering component instances, selectors, simulation insertion global components, simulation state, data flow, life cycle, events, and even simulation routing. Let’s try it out.

Installation:

The way to install Vue Test Utils is not too difficult. Let’s first choose a Test runner, either Jest or Mocha, in this case Jest.

If you haven’t already created a project using vue-CLI, you can select Jest when creating a project using vue-cli, the framework will automatically install Vue Test Utils, run:

 vue create vue-test
Copy the code

If you already have a project created via vue-CLI, you can run:

    vue add @vue/unit-jest
Copy the code

Jest configuration: Jest configuration can be placed in the root directory of Jest. Config. js or Jest

module.exports = {
  preset: "@vue/cli-plugin-unit-jest".// Single test plug-in
  moduleFileExtensions: ["js"."json"."vue"."less"."css"]./ / the suffix
  transform: { // Module parsing
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest".".*\\.(vue)$": "<rootDir>/node_modules/vue-jest",},moduleNameMapper: { // Alias recognition
    "^ @ / (. *) $": "<rootDir>/src/$1"."\\.(css|less)$": "<rootDir>/tests/mocks/styleMock.js",},Jest-serializer-vue is required for snapshot parsing
  snapshotSerializers: ["<rootDir>/node_modules/jest-serializer-vue"].collectCoverage: true.// Coverage directory
  coverageDirectory: "report/coverage".// Jest - html-Reporter must be installed
  reporters: [
    "default"["./node_modules/jest-html-reporter",
      {
        logo: "https://rdc-test.mingyuanyun.com/static/img/rdc.png".pageTitle: "Single test Report (table)".outputPath: "report/unit-test/index.html".includeFailureMsg: true,},],],};Copy the code

Modules to be installed:

  • Jest – Serializer – Vue (Serialization tool)
  • Jest-html-reporter (single test report tool, or other tools can be selected)

Once configured, we can happily run the single test

As shown below, this is a very simple click number increment component:

// Increment.js
<template>
  <div>
    <p>number is {{ count }}</p>
    <button @click="increment">increment</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      count: 0}; },methods: {
    increment() {
      this.count++; ,}}};</script>

<style scoped lang="less">
p {
  font-size: 2em;
  text-align: center;
}
</style>
Copy the code

Vue Test Utils provides methods to implement the wrapper, mount, shallowMount. Once we get the wrapper, we can start using the many interfaces encapsulated in the example

// increment.spec.js
// Import the test toolset
import { mount } from "@vue/test-utils";
import Increment from "@/views/Increment";

describe("Increment".() = > {
  // Mount the component to get the wrapper
  const wrapper = mount(Increment);
  const vm = wrapper.vm;
  it("render markup".() = > {
    expect(wrapper.html()).toContain("<p>number is 0</p>");
  });

  // simulate a user click
  it("button click should increment the count".() = > {
    expect(vm.count).toBe(0);
    const button = wrapper.find("button");
    button.trigger("click");
    expect(vm.count).toBe(1);
  });

  // Click to view dom
  it("button click should increment the count and update the dom".async () => {
    expect(wrapper.text()).toContain("1");
    const button = wrapper.find("button");
    await button.trigger("click");
    expect(wrapper.text()).toContain("2");
  });
});
Copy the code

Once the unit tests are written, let’s execute them:

npm run test:unit
Copy the code

After running, you can view the single test report report/unit-test/index.html in the root directory of our project. Open it in the browser and you can view it

Open coverage/lcov-report/index.html to see coverage

Ok, so now we have a simple unit test case that uses mounts, wrappers, selectors, event triggers, and more, as well as a number of apis that can be seen in the official documentation

The document

Vue Test Utils official document

The article recommended

Unit testing practices in Vue