When learning unit tests, I have been exposed to many concepts, such as Karma, Mocha, Jesmine, Chai, Expect, Assert, should, sinon, etc., which are easily confused. Here I will sort out the concepts.
1. Test frameworks Mocha and Jesmine
1.1 Mocha
Introductory text reference: www.ruanyifeng.com/blog/2015/1…
Mocha is a popular JS testing framework that can be used in browsers and Nodejs environments. Mocha without assertions needs to be used in conjunction with the assertion library. The project also uses a combination of Mocha+ Chai + Sinon. For example, for unit tests written for ndFront components, see github repository: github.com/baihexx/ndf…
Features: flexible, good scalability, can be used with different assertion libraries, but its own integration is not high
1.2 Jesmine
Jesmine is also a commonly used testing framework and was not used in the project.
Features: Built-in assertion library, high integration, convenient support for asynchronous testing, but poor flexibility, single assertion style
2. The assertion
2.1 the assert
The Assert module is built into Node and is used for assertions. Official API Common API
eg:
var assert = requier('assert')
describe('desc1'.function() {
it('desc2'.function() {
assert(a === 1.'The expected value of A is 1'.)})})Copy the code
2.2 should. Js
Github Repository API docs
Should.js is a third-party assertion library, often used in conjunction with Mocha.
- Requier (‘should’): Extend Object. Prototype, add should attribute, all Object can get should directly, eg:
var should = require('should');
(5).should.be.exactly(5).and.be.a.Number();
var a = null
a.should.not.be.ok() / / an error
Copy the code
- Use method 2: If undefined or null, there is no inheritance of Object’s prototype chain, and there is no should attribute available, the following method can be used:
var should = require('should/as-function');
var a = null
should(a).not.be.ok() // pass
should(10).be.exactly(5).and.be.a.Number();
Copy the code
2.3 Chai
Official website API: installation methods and other check the official website
Chai is an assertion library, often used in conjunction with Mocha. There are several assertion styles: Assert, expect, should
- Assert style: Similar to nodeJS’s Assert module (with more write syntax sugar), it is a non-chained language style.
var assert = requier('chai').assert
assert.notEqual(3.4.'these numbers are not equal')
Copy the code
- 2. Expect and should are both BDD styles, a style of chain language, with natural languages such as to,be,been,is, etc.
var expect = requier('Chai').expect
expect([1.2.3]).to.be.an('array').that.includes(2)
Copy the code
- Should () extends Object.prototype by adding the should attribute as follows. Eg:
var should = require('chai').should() //actually call the function
var foo = 'bar'
foo.should.be.a('string')
foo.should.equal('bar')
foo.should.have.lengthOf(3)
Copy the code
(Note that should is not compatible with IE.)
3. Test the running tool: Karma
Making the warehouse
Definition: A simple tool that allows you to execute JavaScript code in multiple real browsers. The main purpose of Karma is to make your test-driven development easy, fast, and fun.
Karma is not a testing framework or assertion library. It starts an HTTP service and generates an Html file from the test file to run and debug in the browser. Karma does not specify a testing framework and can be used in combination with Mocha, Jesmine, and QUnit through plug-ins.
There are many configuration items. It is not difficult to configure according to the official website.
Test coverage: Install and configure as prompted to generate coverage reports
4. Test assistant tool: Sinon
Github repository
Why do we need Sinon? When we do unit testing, we find that the methods we test refer to many external dependencies that we have no control over :(sending mail, network communication, logging, file systems, etc.). For example, front-end projects usually use Ajax to request data from the server, and then do further processing after getting the data. However, when doing unit testing, we often do not actually ask the server for data. Not only is it troublesome, but the server interface may not be ready. This kind of uncertain dependency makes testing complicated. So we need to simulate the process of requesting data, which Sinon uses to solve this problem.
Sinon’s job is essentially a “test surrogate,” which is used to replace parts of the code in tests, making it easy to test complex code.
Sinon provides three features: Examples see the introductory article without further details
- Spy: Provides information about function calls, but does not change the behavior of the function
- Stub: Is similar to spies, but completely replaces the target function. This allows a stubbed function to do whatever you want — throw an exception, return a particular value, etc.
- Mock: Makes it easier to replace a complete object by combining spies and stubs
Eg: admin/misc/user. Js: The user.getuser () function is used to getUser information. The user enters the work number and requests data from the server. Our test case focuses on the front-end code and should not rely on the server to test. (1) The ajax.js request code in nd-SPA is as follows: the actual request function is request.get, so should mock request get function
(2) The test case code is as follows:
- sinon.stub(obj, functionname, mockFun)
- The request code in Ajax.js calls the set(),send(), and end() functions defined in the mock, and does not do the actual back-end request. The end callback returns data directly
- This. Timeout = function (done); otherwise this is window
5. Multi-step test case (actual project record, may not read, estimate can not understand)
In the front page, users usually click the mouse to expect some effect. This process is usually not unit testing, because of the high complexity and rapid change of the page, which is very low cost performance. But some common business components in the project, small changes in demand, step is relatively simple, but use very much, such as social management background in the search user admin/misc/user. The js: user. The autoComplete (), complete process is: the user to enter the user information, Then select the matched user searched, and then click the searched user, the value of the input box becomes the selected user. This process is how to write unit tests. Multi-step simulations are involved.
(1) Util.js encapsulates multi-step execution functions
// utils.js
export function triggerHTMLEvents (target, event, process) {
const e = document.createEvent('HTMLEvents')
e.initEvent(event, true.true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
export function triggerMouseEvents (target, event, process) {
const e = document.createEvent('MouseEvents')
e.initEvent(event, true.true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
export function triggerUIEvents (target, event, process) {
const e = document.createEvent('UIEvents')
e.initEvent(event, true.true)
if (process) process(e)
target.dispatchEvent(e)
return e
}
// timeout: The execution interval can be set
function createStep ({ step, timeout }) {
return new Promise((resolve, reject) = > {
setTimeout((a)= > {
try {
resolve(step())
} catch (err) {
reject(err)
}
}, timeout || 0)})}// Multi-step execution function, execution steps encapsulated in arR array
export function runSteps (arr) {
if (arr.length === 0) {
return
}
let firstStep = createStep(arr[0])
const others = arr.splice(1)
others.forEach(item= > {
firstStep = firstStep.then((a)= > {
return createStep(item)
})
})
}
Copy the code
(2) Use
There is an additional problem. Note that the getUsers function in note (1) user uses ucOrgId, (var ucOrgId = auth.getauth (‘uc_org_id’) and auth.setauth ()); And set it to auth.
If user.spec.js is set to auth, then auth.setauth (…) , the result is incorrect. Reason: The ucOrgId function is implemented at the beginning of user.js. We introduced auth and user first in the test code. At this time, ucOrgId has been obtained and is empty. Always execute import first, resulting in function execution later. The solution is to write the function that sets login data in a separate file, use import method, and inport before user, to achieve the effect of executing login data first. (See the code for more clarity)