Detailed description of Vue unit test Karma + Mocha study notes, karmamocha
When you use vue-cli to create a project, you are prompted whether to install the unit test and e2e test. Since the two test frameworks are officially recommended, let's learn and practice them.
Introduction
Karma
Karma is a Node. js-based JavaScript Test execution process management tool (Test Runner ). This tool is mainly used in Vue to run projects in various mainstream Web browsers for testing.
In other words, it is a test tool that allows your code to be tested in a browser environment. The reason for this is that your code may be designed to be executed on the browser, and some bugs may not be exposed in the node environment. In addition, there are browser compatibility problems, karma provides a means to automatically run your code in multiple browsers (chrome, firefox, ie, etc. If your code only runs on the node, you do not need to use karma.
Mocha
Mocha is a testing framework that works with the chai assertion library in vue-cli to implement unit testing.
The Chai assertion library can read the Chinese documentation of the Chai. js assertion library API, which is simple and can be quickly mastered by multiple queries and usage.
My understanding of the test framework
Npm run unit Execution Process
- Run the npm run unit command
- Enable Karma Runtime Environment
- Use Mocha to test the test cases written with Chai assertions one by one
- Display test results on the terminal
- If the test succeeds, karma-coverage will generate a webpage for the test coverage result in the./test/unit/coverage folder.
Karma
For Karma, I just learned about its configuration options.
The following is the karma configuration of Vue. A simple comment is given below:
Var webpackConfig = require ('.. /.. /build/webpack. test. conf ') module. exports = function (config) {config. set ({// browser browsers: ['phantomjs'], // test framework frameworks: ['mocha ', 'sinon-chai', 'phantomjs-shim'], // test report reporters: ['spec ', 'coverage'], // test entry file files :['. /index. js'], // pre-processor karma-webpack preprocessors :{'. /index. js': ['webpackage', 'sourcemap']}, // webpack configuration Webpack: webpackConfig, // webpack middleware webpackMiddleware: {noInfo: true }, // test coverage report // https://github.com/karma-runner/karma-coverage/blob/master/docs/configuration.md coverageReporter: {dir :'. /coverage ', reporters: [{type: 'lcov', subdir :'. '},{ type: 'text-summary'}] }})}
Mocha and chai
Let's take a look at the official example (I used comments to explain the meaning of the Code ):
Import Vue from 'vue '// import vue to generate Vue instance import Hello from' @/components/hello' // import Component // The test script should contain one or more describe block, called test suite describe ('Hello. vue ', () => {// each describe block should contain one or more it blocks, called test case it ('could render correct CONTENTS ',() ==>{ const Constructor = Vue. extend (Hello) // get the Hello component instance const vm = new Constructor (). $ mount () // hook the component to the DOM // asserted that the text content of the h1 element in the element of class hello in the DOM is Welcome to Your Vue. js App reverse CT (vm. $ el. querySelector ('. hello h1 '). textContent ). to. equal ('Welcome to Your Vue. js App ')})})
Knowledge points to be known:
- Test scripts must be stored in the test/unit/specs/directory.
- The script name is [component name]. spec. js.
- The so-called assertion is to perform operations on components and predict the results. If the test result is the same as the assertion, the test passes.
- By default, all files except main. js under the src directory can be modified in the test/unit/index. js file.
- In the Chai assertion library, the language chain to be been is that which and has have with at of same is meaningless and easy to understand.
- The test script consists of multiple descibe, and each describe is composed of multiple it.
- Measure the test taker's understanding about asynchronous testing.
It ('asynchronous request should return an object', done => {request. get ('https: // api.github.com '). end (function (err, res) {CT (res ). to. be. an ('object'); done ();});});
About describe hooks (lifecycle)
Describe ('hooks', function () {before (function () {// run before all test cases in this block}); after (function () {// execution after all test cases in this block}); beforeEach (function () {// execution before each test case in this block}); afterEach (function () {// execute after each test case in this block}); // test cases });
Practice
The usage of unit testing is briefly introduced above. Next we will start unit testing in Vue!
Util. js
From the official Vue demo, we can see that for Vue unit testing, we need to instantiate the component as a Vue instance and sometimes mount it to the DOM.
Const Constructor = Vue. extend (Hello) // get the Hello component instance const vm = new Constructor (). $ mount () // mount the component to the DOM
The above code is just a simple way to get components. Sometimes we need to pass props attributes, custom methods, etc. We may also need to use a third-party UI framework. Therefore, the above writing is very troublesome.
Here we recommend the Element unit test tool script Util. js, which encapsulates common methods in Vue unit test. The demo below is also written based on Util. js.
The usage of each method is briefly described here.
/*** Reclaim the vm. Generally, the vm is recycled after each test script is tested. * @ Param {Object} vm */exports. destroyVM = function (vm) {}/ *** create a Vue Instance Object * @ param {Object | String} Compo-component configuration, you can directly upload template * @ param {Boolean = false} mounted-add to DOM * @ return {Object} vm */exports. createVue = function (Compo, mounted = false) {}/*** create a test component instance * @ param {Object} Compo-Component Object * @ param {Object} propsData-props data * @ param {Boolean = false} mounted -Add to DOM * @ retu Rn {Object} vm */exports. createTest = function (Compo, propsData = {}, mounted = false) {}/*** trigger an event * Note: vm is generally used after an event is triggered. the $ nextTick method is used to determine whether the event is triggered successfully. * Mouseenter, mouseleave, mouseover, keyup, change, click * @ param {Element} elm-Element * @ param {String} name-event name * @ param {*} opts-configuration item */exports. triggerEvent = function (elm, name ,... opts) {}/*** triggers the "mouseup" and "mousedown" events, both triggering click events. * @ Param {Element} elm-Element * @ param {*} opts-configuration option */exports. triggerClick = function (elm,... opts ){}
Example 1
In example 1, we tested the data of various elements of the Hello component, learned how to use the destroyVM and createTest methods of util. js, and how to obtain the target element for testing. To obtain DOM elements, you can view the DOM object tutorial.
Hello. vue
<template> <div class="hello">
Hello. spec. js
Import {destroyVM, createTest} from '.. /util 'import Hello from' @/components/hello' describe ('Hello. vue ', () =>{ let vm afterEach () =>{ destroyVM (vm)}) it ('test to obtain element content ',() ==>{ vm = createTest (Hello, {content: 'Hello world'}, true) reverse CT (vm. $ el. querySelector ('. hello h1 '). textContent ). to. equal ('Welcome! ') Reverse CT (vm. $ el. querySelector ('. hello h2 '). textContent ). to. have. be. equal ('Hello World')}) it ('test to obtain data in Vue object', () =>{ vm = createTest (Hello, {content: 'Hello world '}, true) reverse CT (vm. msg ). to. equal ('Welcome! ') // The Chai language chain is meaningless and can be written at will. CT (vm. content ). which. have. to. be. that. equal ('Hello World')}) it ('test whether a class exists in DOM ', () =>{ vm = createTest (Hello, {content: 'Hello world'}, true) Round CT (vm. $ el. classList. contains ('hello ')). to. be. true const title = vm. $ el. querySelector ('. hello h1 ') Round CT (title. classList. contains ('hello-title ')). to. be. true const content = vm. $ el. querySelector ('. hello-content') Precise CT (content. classList. contains ('hello-content ')). to. be. true })})
Output result
Hello. vue
√ Test to obtain Element Content
√ Test to obtain data in a Vue object
√ Test whether a class exists in the DOM.
Example 2
In Example 2, we use createTest to create a test component test Click Event and createVue to create a Vue sample object test component Click. The following describes how to use the createVue method.
Click. vue
<Template> <div> <span class = "init-num"> Initial Value: {InitNum }}</span> <br> <span class = "click-num"> {ClickNum} clicks </span> <br> <span class = "result-num"> the final result is {ResultNum }}</span> <br> <button @ click = "add"> accumulate {AddNum }}</button> </div> </template> <script> export default {name: 'click', props: {AddNum: {type: Number, default: 1}, InitNum: {type: Number, default: 1 }}, data () {return {ClickNum: 0, ResultNum: 0 }}, mounted () {this. resultNum = this. initNum}, methods: {add () {this. resultNum + = this. addNum this. clickNum ++ this. $ emit ('result', {ClickNum: this. clickNum, ResultNum: this. resultNum}) }}</script>
Click. spec. js
Import {destroyVM, createTest, createVue} from '.. /util 'import Click from' @/components/click' describe ('click. vue ', () =>{ let vm afterEach () =>{ destroyVM (vm)}) it ('test button click event ',() ==>{ vm = createTest (Click, {AddNum: 10, InitNum: 11}, true) let buttonElm = vm. $ el. querySelector ('button ') buttonElm. click () buttonElm. click () buttonElm. click () // setTimeout cause // after the data changes, the interface changes will be delayed. Without timeout, you may find that the interface has not changed. setTimeout (done => {reset CT (vm. resultNum ). to. equal (41) reverse CT (vm. $ el. querySelector ('. init-num '). textContent ). to. equal ('initial value: 11') reverse CT (vm. $ el. querySelector ('. click-num '). textContent ). to. equal ('clicked three times ') reverse CT (vm. $ el. querySelector ('. result-num '). textContent ). to. equal ('final result: 41') done ()}, 100)}) it ('test creation Vue object ',() ==>{ let result vm = createVue ({template: '<click @ click = "handleClick"> </click>', props: {AddNum: 10, InitNum: 11 }, methods: {handleClick (obj) {result = obj}, components: {Click}, true) vm. $ el. click () vm. $ nextTick (done =>{ expect CT (result ). to. be. exist exact CT (result. clickNum ). to. equal (1) until CT (result. resultNum ). to. be. equal (21) done ()})})
Output result
Click. vue
√ Test button click event
√ Test Vue object Creation
Others
All the sample code can be easily viewed in the Github repository. If you want to see more good test cases, we suggest you use Util. js to check the unit test script of Element. There are many test scripts for us to learn. As the UI component library used by Vue users, the test script must be well written ~ You can even copy these scripts. I believe this will be of great help in learning the unit test of Vue components.
The following are my notes on Element unit test for your reference.
The Util. js method includes the majority of Vue componentized testing requirements.
Vm. $ el vm. $ nextTick and vm. $ ref are common Vue syntax sugar during testing.
Note: The vm. $ nextTick method is asynchronous, so you need to use the done method in it.
Asynchronous assertion. The method parameter must be _ or done.
Most of the time, the query element is obtained by querying the class using the querySelector method.
vm.$el.querySelector('.el-breadcrumb').innerText
In most cases, the classList. contains method is used to query whether a Class exists. The result is true or false.
vm.$el .classList.contains('el-button--primary')
The asynchronous test must end with the done () method. SetTimeout and vm. $ nextTick are common asynchronous tests.
Click implement button: Obtain the btn element and execute the btn. click () method.
Because Vue asynchronously updates the DOM, some assertions that depend on the DOM update result must be performed in the Vue. nextTick callback.
triggerEvent(vm.$refs.cascader.$el, 'mouseenter');vm.$nextTick(_ => { vm.$refs.cascader.$el.querySelector('.el-cascader__clearIcon').click(); vm.$nextTick(_ => { expect(vm.selectedOptions.length).to.be.equal(0); done(); });});
Vue. js Learning Series Project address: https://github.com/violetjack/VueStudyDemos
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.