Why unit tests

Unit tests are used to test the functionality of a module in a project, such as functions, classes, components, etc. Unit tests do the following:

  • Correctness: it can verify the correctness of the code and make more detailed preparation before going online;
  • Automation: Test cases can be integrated into code versioning, automatic unit testing, avoiding every manual operation;
  • Explanatory: Provides other developers with documentation of the module under test, and reading test cases may be more complete than the documentation;
  • Drive development and guide design: Pre-written unit tests can guide the API design of development, and can also find problems in the design in advance;
  • Ensure refactoring: Test cases can be validated multiple times, saving a lot of time when regression testing is required.

How to write unit tests

The test principles

  • When testing code, only tests are considered, not internal implementations
  • The data should be as close to reality as possible
  • Take full account of the boundary conditions of the data
  • Key, complex, core code, focus on testing
  • The combination of testing and functional development facilitates design and code refactoring

Write the steps

  1. Preparation phase: Construct parameters, create spy, etc
  2. Execution phase: Executes the code under test with constructed parameters
  3. Assertion phase: Compare the actual results with the expected results to determine if the test is healthy
  4. Cleanup phase: Clears the impact of the preparation phase on the external environment and removes the Spy created during the preparation phase

Three, test tools

Unit testing tools fall into three categories:

  • Test Runner: it can simulate various browser environments and customize Test frameworks and assertion libraries, such as Karma.
  • Testing frameworks: Functional modules that provide unit testing. Common frameworks include Jest, Mocha, Jasmine, and QUnit.
  • Assert, should. Js, expect. Js, chai. Js, enzyme render library, Istanbul coverage calculation.

Here, we’ll use Jest as an example. Jest features comprehensive, integrated with a variety of tools, and easy to configure, even zero configuration direct use.

Introduction to Jest

The description on Jest’s website reads as follows:

Jest is a delightful JavaScript Testing Framework with a focus on simplicity.

The installation

yarn add --dev jest
# or
# npm install -D jest
Copy the code

A simple example

Starting with the example provided on the official website, test a function that adds two numbers to create a sum.js file

function sum(a, b) {
  return a + b;
}
module.exports = sum;
Copy the code

Then, create the sum.test.js file

const sum = require('./sum');

test('adds 1 + 2 to equal 3'.() = > {
  expect(sum(1.2)).toBe(3);
});
Copy the code

Add a test task to package.json:

{
  "scripts": {
    "test": "jest"}}Copy the code

Finally, run YARN test or NPM run test, and Jest prints the following message:

PASS./sum. Test. Js ✓ Adds 1 + 2 to equal 3 (5ms)Copy the code

At this point, a basic unit test is complete.

Note: Jest emulates a real browser by using JSDOM in the Node virtual browser environment. Since js emulates the DOM, Jest cannot test the style. The Jest test runner automatically sets up the JSDOM.

Jest Cli

You can run Jest directly from the command line (provided it has been added to the environment variable PATH, such as Jest installed through YARN Global add Jest or NPM install Jest –global) and specify various useful configuration items for it. Such as:

jest my-test --notify --config=config.json
Copy the code

The Jest command has the following common parameters:

  • --coverageRepresents the output unit test coverage. The coverage file defaults totests/unit/coverage/lcov-report/index.html;
  • --watchListening mode, retriggering unit tests whenever files related to test cases change.

See Jest CLI Options for more Options.

Using configuration files

Use the jest command to generate a configuration file:

jest --init
Copy the code

There are several options for you to choose from:

√ Would you like to use Typescript forthe configuration file? . No) Choose thetest environment that will be used forTesting » JsDOM (Browser-like) √ Do you want Jest to add coverage reports? . Yes √ Which provider should be used to instrument codefor coverage? » babel
√ Automatically clear mock calls and instances between every test? . yesCopy the code

Example configuration file (not based on the above selection) :

// jest.config.js
const path = require('path')

module.exports = {
    preset: '@vue/cli-plugin-unit-jest/presets/typescript-and-babel'.rootDir: path.resolve(__dirname, '/'),
    coverageDirectory: '<rootDir>/tests/unit/coverage'.collectCoverageFrom: [
        'src/*.{js,ts,vue}'.'src/directives/*.{js,ts,vue}'.'src/filters/*.{js,ts,vue}'.'src/helper/*.{js,ts,vue}'.'src/views/**/*.{js,ts,vue}'.'src/services/*.{js,ts,vue}']}Copy the code

The use of Babel

yarn add --dev babel-jest @babel/core @babel/preset-env
Copy the code

You can create a babel.config.js file in the root directory of your project to configure Babel compatible with your current Node version:

// babel.config.js
module.exports = {
  presets: [['@babel/preset-env', {targets: {node: 'current']]}}};Copy the code

Vue-cli using Jest

Jest can be used in vue-cli by installing @vue/cli-plugin-unit-jest plug-in in the project:

vue add unit-jest
# or
# yarn add -D @vue/cli-plugin-unit-jest @types/jest
Copy the code
"scripts": {
    "test:unit": "vue-cli-service test:unit --coverage"
},
Copy the code

@vue/cli-plugin-unit-jest will inject the command test:unit into vue-cli-service, which will recognize the following files by default: < rootDir > / tests/unit / * * / *. Spec. (js | JSX | | ts TSX) | / __tests__ / *. * * (js | JSX | | ts TSX)) performing unit tests, Which tests/unit directory. The spec. (js | JSX | | ts TSX) at the end of the file and directory called all the js in the __tests__ / ts (x) (x) documents.

Common example

Judge value equal

  • toBe()Check if the two base types match exactly:
test('two plus two is four'.() = > {
  expect(2 + 2).toBe(4);
});
Copy the code
  • toEqual()Check whether objects are equal:
test('object assignment'.() = > {
  const data = {one: 1};
  data['two'] = 2;
  expect(data).toEqual({one: 1.two: 2});
});
Copy the code

Check the class false value

  • toBeNullMatches only null
  • toBeUndefinedMatches only undefined
  • toBeDefinedIn contrast to the toBeUndefined
  • toBeTruthyMatches any if statement to true
  • toBeFalsyMatches any if statement false

Example:

test('null'.() = > {
  const n = null;
  expect(n).toBeNull();
  expect(n).toBeDefined();
  expect(n).not.toBeUndefined();
  expect(n).not.toBeTruthy();
  expect(n).toBeFalsy();
});

test('zero'.() = > {
  const z = 0;
  expect(z).not.toBeNull();
  expect(z).toBeDefined();
  expect(z).not.toBeUndefined();
  expect(z).not.toBeTruthy();
  expect(z).toBeFalsy();
});
Copy the code

Numeric size comparison

test('two plus two'.() = > {
  const value = 2 + 2;
  expect(value).toBeGreaterThan(3);
  expect(value).toBeGreaterThanOrEqual(3.5);
  expect(value).toBeLessThan(5);
  expect(value).toBeLessThanOrEqual(4.5);

  // toBe and toEqual are equivalent for numbers
  expect(value).toBe(4);
  expect(value).toEqual(4);
});
Copy the code

For comparing floating-point equality, use toBeCloseTo instead of toEqual, because you don’t want the test to depend on a small rounding error.

test('Add two floating point numbers'.() = > {
  const value = 0.1 + 0.2;
  / / expect (value). Place (0.3); This is an error because floating point numbers have rounding errors
  expect(value).toBeCloseTo(0.3); // This sentence can run
});
Copy the code

String comparison

You can use regular expressions to check:

test('there is no I in team'.() = > {
  expect('team').not.toMatch(/I/);
});

test('but there is a "stop" in Christoph'.() = > {
  expect('Christoph').toMatch(/stop/);
});
Copy the code

Arrays and class arrays

You can check whether an array or iterable contains a particular item by using toContain:

const shoppingList = [
  'diapers'.'kleenex'.'trash bags'.'paper towels'.'milk',]; test('the shopping list has milk on it'.() = > {
  expect(shoppingList).toContain('milk');
  expect(new Set(shoppingList)).toContain('milk');
});
Copy the code

abnormal

It can also be used to check if a function throws an exception:

function compileAndroidCode() {
  throw new Error('you are using the wrong JDK');
}

test('compiling android goes as expected'.() = > {
  expect(() = > compileAndroidCode()).toThrow();
  expect(() = > compileAndroidCode()).toThrow(Error);

  // You can also use the exact error message or a regexp
  expect(() = > compileAndroidCode()).toThrow('you are using the wrong JDK');
  expect(() = > compileAndroidCode()).toThrow(/JDK/);
});
Copy the code

Refer to the API documentation for more information.

Only the current test is executed

The only() method can be used to indicate that only the test is executed, reducing unnecessary duplicate tests:

test.only('it is raining'.() = > {
  expect(inchesOfRain()).toBeGreaterThan(0);
});

test('it is not snowing'.() = > {
  expect(inchesOfSnow()).toBe(0);
});
Copy the code

Testing asynchronous code

The callback function

For example, suppose you have a fetchData(callback) function that fetches some data and calls callback(data) when it’s done. The data you expect to return is a string ‘peanut butter’ :

test('the data is peanut butter'.done= > {
  function callback(data) {
    try {
      expect(data).toBe('peanut butter');
      done();
    } catch (error) {
      done(error);
    }
  }

  fetchData(callback);
});
Copy the code

Done () is used to indicate that the test is done. Without done(), our unit test will end after the test is done, which is not expected because callback has not been called and the unit test has not finished. If the done() function is never called, a timeout error will be displayed.

If Expect fails, it throws an error and done() is no longer executed. If we want to know why a test case fails, we must put Expect into a try, passing the error to the done function in the catch. Otherwise, the console will eventually display a timeout error failure and fail to display the values we received in Expect (Data).

Promises

Again, use the above example:

test('the data is peanut butter'.() = > {
  return fetchData().then(data= > {
    expect(data).toBe('peanut butter');
  });
});
Copy the code

Never forget the return result to ensure that the test and function end at the same time.

If a Promise is expected to be rejected, catch is used:

test('the fetch fails with an error'.() = > {
  expect.assertions(1);
  return fetchData().catch(e= > expect(e).toMatch('error'));
});
Copy the code

It is also possible to use conversions and rejects:

test('the data is peanut butter'.() = > {
  return expect(fetchData()).resolves.toBe('peanut butter');
});
Copy the code
test('the fetch fails with an error'.() = > {
  return expect(fetchData()).rejects.toMatch('error');
});
Copy the code

Async/Await

test('the data is peanut butter'.async() = > {const data = await fetchData();
  expect(data).toBe('peanut butter');
});

test('the fetch fails with an error'.async () => {
  expect.assertions(1);
  try {
    await fetchData();
  } catch (e) {
    expect(e).toMatch('error'); }});Copy the code

Async /await can also be combined with high similarity ()/rejects() :

test('the data is peanut butter'.async() = > {await expect(fetchData()).resolves.toBe('peanut butter');
});

test('the fetch fails with an error'.async() = > {await expect(fetchData()).rejects.toMatch('error');
});
Copy the code

Installation and disassembly

Before and after the test

In some cases, we need to do some preparatory work before we start the test, and then some cleanup after the test is complete, using beforeEach and afterEach.

For example, we need to initialize some city data before each test and clean it up after the test:

beforeEach(() = > {
  initializeCityDatabase();
});

afterEach(() = > {
  clearCityDatabase();
});

test('city database has Vienna'.() = > {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan'.() = > {
  expect(isCity('San Juan')).toBeTruthy();
});
Copy the code

Similarly, beforeAll and afterAll, single executions before and after the current spec test file starts and ends.

Test case grouping

By default, blocks of before and after can be applied to each test in the file. Tests can also be grouped through the Describe block. When the before and after blocks are inside the Describe block, they only apply to tests within the Describe block.

// Applies to all tests in this file
beforeEach(() = > {
  return initializeCityDatabase();
});

test('city database has Vienna'.() = > {
  expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan'.() = > {
  expect(isCity('San Juan')).toBeTruthy();
});

describe('matching cities to foods'.() = > {
  // Applies only to tests in this describe block
  beforeEach(() = > {
    return initializeFoodDatabase();
  });

  test('Vienna <3 sausage'.() = > {
    expect(isValidCityFoodPair('Vienna'.'Wiener Würstchen')).toBe(true);
  });

  test('San Juan <3 plantains'.() = > {
    expect(isValidCityFoodPair('San Juan'.'Mofongo')).toBe(true);
  });
});
Copy the code

Execution order

Because of the grouping using Describe, there are nested scopes that are executed in the following order for each lifecycle:

  • Of the outer scopebeforeExecute before the inner, while after does the opposite;
  • At the same levelbeforeAllbeforeEachExecute first, after the reverse;
beforeAll(() = > console.log('1 - beforeAll'));
afterAll(() = > console.log('1 - afterAll'));
beforeEach(() = > console.log('1 - beforeEach'));
afterEach(() = > console.log('1 - afterEach'));
test(' '.() = > console.log('1 - test'));
describe('Scoped / Nested block'.() = > {
  beforeAll(() = > console.log('2 - beforeAll'));
  afterAll(() = > console.log('2 - afterAll'));
  beforeEach(() = > console.log('2 - beforeEach'));
  afterEach(() = > console.log('2 - afterEach'));
  test(' '.() = > console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll
Copy the code

A mock function

Jest. Fn () can be used to generate a mock function, and jest can capture the function call, this, return value, and so on, which is useful when testing callback functions.

To test the mock

Suppose we want to test the internal implementation of the function forEach, which calls the callback once forEach element in the passed array.

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) { callback(items[index]); }}Copy the code

To test this function, we can use a mock function and then check the state of the mock function to ensure that the callback function is called as expected.

const mockCallback = jest.fn(x= > 42 + x);
forEach([0.1], mockCallback);

// This mock function is called twice
expect(mockCallback.mock.calls.length).toBe(2);

// The first argument to the function is 0
expect(mockCallback.mock.calls[0] [0]).toBe(0);

// The second time the function is called, the first argument is 1
expect(mockCallback.mock.calls[1] [0]).toBe(1);

// The return value of the first function call is 42
expect(mockCallback.mock.results[0].value).toBe(42);
Copy the code

The return value of the mock

Mock functions can also be used to inject test values into code during testing:

const myMock = jest.fn();
console.log(myMock());
// > undefined

myMock.mockReturnValueOnce(10).mockReturnValueOnce('x').mockReturnValue(true);

console.log(myMock(), myMock(), myMock(), myMock());
// > 10, 'x', true, true
Copy the code

Analog interface return

Suppose you have a class that gets users from an API. This class calls the API using AXIos and returns data, which contains attributes for all users:

// users.js
import axios from 'axios';

class Users {
  static all() {
    return axios.get('/users.json').then(resp= >resp.data); }}export default Users;
Copy the code

Now, to test the method without actually calling the API (making the test slow and vulnerable), we can use jest. Mock (…) Function automatically emulates the AXIOS module. Once the module is emulated, we can provide a mockResolvedValue for.get, which will return dummy data for testing.

// users.test.js
import axios from 'axios';
import Users from './users';

jest.mock('axios');

test('should fetch users'.() = > {
  const users = [{name: 'Bob'}];
  const resp = {data: users};
  axios.get.mockResolvedValue(resp);

  // or you could use the following depending on your use case:
  // axios.get.mockImplementation(() => Promise.resolve(resp))

  return Users.all().then(data= > expect(data).toEqual(users));
});
Copy the code

The matcher of the mock function

With mock, you can add some custom matchers to a function:

// The mock function was called at least once
expect(mockFunc).toHaveBeenCalled();

// The mock function was called at least once with the specified args
expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);

// The last call to the mock function was called with the specified args
expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);

// All calls and the name of the mock is written as a snapshot
expect(mockFunc).toMatchSnapshot();
Copy the code

You can also simulate it yourself with a native matcher. The code below is equivalent to the code above:

// The mock function was called at least once
expect(mockFunc.mock.calls.length).toBeGreaterThan(0);

// The mock function was called at least once with the specified args
expect(mockFunc.mock.calls).toContainEqual([arg1, arg2]);

// The last call to the mock function was called with the specified args
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1]).toEqual([
  arg1,
  arg2,
]);

// The first arg of the last call to the mock function was `42`
// (note that there is no sugar helper for this specific of an assertion)
expect(mockFunc.mock.calls[mockFunc.mock.calls.length - 1] [0]).toBe(42);

// A snapshot will check that a mock was invoked the same number of times,
// in the same order, with the same arguments.
expect(mockFunc.mock.calls).toEqual([[arg1, arg2]]);
expect(mockFunc.getMockName()).toBe('a mock name');
Copy the code

Vue Test Utils

Vue Test Utils:

Vue Test Utils is the official Vue. Js unit Test utility library.

The following examples are based on vue-CLI scaffolding, including Webpack/Babel/Vue-loader

Test the single-file component

Vue’s single-file components need to be precompiled before they run in Node or a browser. We recommend two ways to complete the compilation: through a Jest precompiler, or directly using WebPack. Here we use Jest.

yarn add -D jest @vue/test-utils vue-jest
Copy the code

Vue-jest does not currently support all of vue-Loader’s features, such as custom blocks and style loading. Additional webpack-specific features such as code separation are also not supported. To use these unsupported features, you need to run your tests with Mocha instead of Jest and compile your components with WebPack.

Handle webpack aliases

Vue-cli uses @ as the/SRC alias by default. Jest also needs to be configured separately:

// jest.config.js

module.exports = {
    moduleNameMapper: {
        '^ @ / (. *) $': '<rootDir>/src/$1'}}Copy the code

Mount components

Mounted components are returned to a wrapper, which exposes many convenient ways to encapsulate, traverse, and query Vue component instances within it.

// test.js

// Import the 'mount()' method from the test utility set
Import the component you want to test at the same time
import { mount } from '@vue/test-utils'
import Counter from './counter'

// Now mount the component and you get the wrapper
const wrapper = mount(Counter)

// You can access the actual Vue instance via 'wrapper.vm'
const vm = wrapper.vm

// Review the wrapper in depth by recording it on the console
// This is where our exploration of Vue Test Utils began
console.log(wrapper)
Copy the code

While mounting, you can set various properties of the component:

const wrapper = mount(Counter, {
    localVue,
    data() {
        return {
            bar: 'my-override'}},propsData: {
        msg: 'abc'
    },
    parentComponent: Foo, // Specify the parent component
    provide: {
        foo() {
            return 'fooValue'}}})Copy the code

Test the HTML rendered by the component

Determine whether the HTML rendered by the component is as expected using the Wrapper method.

import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter'.() = > {
  // Now mount the component and you get the wrapper
  const wrapper = mount(Counter)

  test('renders the correct markup'.() = > {
    expect(wrapper.html()).toContain('<span class="count">0</span>')})// It is also easy to check existing elements
  test('has a button'.() = > {
    expect(wrapper.contains('button')).toBe(true)})})Copy the code

Simulating user operations

When the user clicks the button, our counter should be incremented. To simulate this behavior, we first need to locate the button via wrapper.find(), which returns a wrapper for the button element. We can then simulate clicking by calling.trigger() on the button wrapper.

it('button click should increment the count'.() = > {
  expect(wrapper.vm.count).toBe(0)
  const button = wrapper.find('button')
  button.trigger('click')
  expect(wrapper.vm.count).toBe(1)})Copy the code

To test whether the text in the counter has been updated, we need to know about nextTick. Any changes that cause manipulation of the DOM should await the nextTick function before asserting.

it('button click should increment the count text'.async () => {
  expect(wrapper.text()).toContain('0')
  const button = wrapper.find('button')
  await button.trigger('click')
  expect(wrapper.text()).toContain('1')})Copy the code

Component events

Each mounted wrapper automatically logs all triggered events through the Vue instance behind it. You can retrieve such event logs with the wrapper.emitted() method.

wrapper.vm.$emit('foo')
wrapper.vm.$emit('foo'.123)

` / * ` wrapper. Emitted () returns the following objects: {foo: [[], [123]]} * /
Copy the code

You can then set assertions based on this data:

// The assertion event has been raised
expect(wrapper.emitted().foo).toBeTruthy()

// Assert the number of events
expect(wrapper.emitted().foo.length).toBe(2)

// Assert valid data for the event
expect(wrapper.emitted().foo[1]).toEqual([123])
Copy the code

You can also fire events for child components:

import { mount } from '@vue/test-utils'
import ParentComponent from '@/components/ParentComponent'
import ChildComponent from '@/components/ChildComponent'

describe('ParentComponent'.() = > {
  test("displays 'Emitted! ' when custom event is emitted".() = > {
    const wrapper = mount(ParentComponent)
    wrapper.find(ChildComponent).vm.$emit('custom')
    expect(wrapper.html()).toContain('Emitted! ')})})Copy the code

Components of the data

You can use setData() or setProps to set the component’s state data:

it('manipulates state'.async() = > {await wrapper.setData({ count: 10 })

  await wrapper.setProps({ foo: 'bar'})})Copy the code

Simulate vUE instance method

Since setMethods() of Vue Test Utils are about to be deprecated, it is recommended to use the jest.spyon () method to simulate Vue instance methods:

import MyComponent from '@/components/MyComponent.vue'

describe('MyComponent'.() = > {
  it('click does something'.async() = > {const mockMethod = jest.spyOn(MyComponent.methods, 'doSomething')
    await shallowMount(MyComponent).find('button').trigger('click')
    expect(mockMethod).toHaveBeenCalled()
  })
})
Copy the code

Global plugin

If you need to install a global plugin that all tests use, you can use setupFiles, specifying the setup file in jest.config.js first:

// jest.config.js
module.exports = {
    setupFiles: ['<rootDir>/tests/unit/setup.js']}Copy the code

Then in setup.js use:

// setup.js
import Vue from 'vue'

// The following globally registered plug-ins do not take effect in Jest and must use localVue
import ElementUI from 'element-ui'
import VueClipboard from 'vue-clipboard2'

Vue.use(ElementUI)
Vue.use(VueClipboard)

Vue.config.productionTip = false
Copy the code

LocalVue is used when you just want to install global plug-ins in some tests, which creates a temporary Vue instance:

import { createLocalVue, mount } from '@vue/test-utils'

// Create an extended 'Vue' constructor
const localVue = createLocalVue()

// Install the plug-in normally
localVue.use(MyPlugin)

// Pass 'localVue' in the mount option
mount(Component, {
  localVue
})
Copy the code

Test the watch

Suppose we had a Watcher that looked like this:

watch: {
  inputValue(newVal, oldVal) {
    if(newVal.trim().length && newVal ! == oldVal) {console.log(newVal)
    }
  }
}
Copy the code

Since the call of watch is asynchronous and will not be called until the next tick, you can check whether the watch is effective by checking whether the methods in watcher are called, using the jest.spyon () method:

describe('Form.test.js'.() = > {
  let cmp
  ...

  describe('Watchers - inputValue'.() = > {
    let spy

    beforeAll(() = > {
      spy = jest.spyOn(console.'log')
    })

    afterEach(() = > {
      spy.mockClear()
    })

    it('is not called if value is empty (trimmed)'.() = > {
    })

    it('is not called if values are the same'.() = > {
    })

    it('is called with the new value in other cases'.() = >{})})})Copy the code
it("is called with the new value in other cases".done= > {
  cmp.vm.inputValue = "foo";
  cmp.vm.$nextTick(() = > {
    expect(spy).toBeCalled();
    done();
  });
});
Copy the code

Third-party plug-ins

When we use some third-party plugins, we don’t need to worry about their internal implementation and don’t need to test their components. We can use shallowMount instead of mount to reduce unnecessary rendering:

import { shallowMount } from '@vue/test-utils'

const wrapper = shallowMount(Component)
wrapper.vm // Mount the Vue instance
Copy the code

Third party components can also be found by findAllComponents:

import { Select } from 'element-ui'
test('Do not show branches and branches when headquarters is selected'.async() = > {await wrapper.setProps({
        value: {
            clusterType: 'head-quarter-sit'.branch: ' '.site: ' '}})// Headquarters does not display branches and branches
    expect(wrapper.findAllComponents(Select)).toHaveLength(1)})Copy the code

Six, summarized

Unit testing theory

  • Unit tests can continuously verify the correctness of code, drive development, and play a certain role of documentation;
  • Test data as far as possible to simulate reality, only consider the test, not the internal code;
  • Take full account of the boundary conditions of the data when testing
  • Key, complex, core code, focus on testing
  • Writing unit tests has the following phases: preparation, execution, assertion, cleanup;
  • Unit testing tools fall into three categories: Test runners, Test frameworks, and tool libraries.

Jest

  • --watchOption to listen to the encoding of files and automatically execute unit tests;
  • Testing asynchronous code can be useddoneMethod or AYNC function;
  • Mock functions capture the function call, this, return value, and so on, which are useful when testing callback functions.

Vue Test Utils

  • withmountMethod to mount components and customize various VUE properties;
  • shallowMountMethod does not render subcomponents to speed up testing;
  • setupFilesYou can set up the global environment, such as installationelement-ui;
  • createLocalVueCan create a separate VUE instance, isolated from the global;