Preface
Although the popularity of front-end testing is not high , But it has a great effect on the quality of the project code and the later maintenance project . In this paper, I will talk about Jest unit testing , Because a lot of API See the official website for details .
What is? Jest
Jest It's used to create 、 Execute and build one of the test cases JavaScript test
library . It can be installed and used in any project , Such as Vue/React/Angular/Node/TypeScript etc. .
Use Jest The benefits of
At present, the front-end test is mainly used in the development and application scenarios UI Component library , But it's not just component libraries , Unit testing can also be added to daily project development , Ensure the stability of the project .
What are the benefits of it ?
- Fast , Easy to use and easy to configure
- By writing tests , Let the code appear bug It's less likely
- It's good for writing robust code , Project maintainability enhancement
- Good test , It has the function of document explanation
- Reduce... In regression testing bug
Take a chestnut
For example, write an addition and subtraction function
function add(a, b) {
return a + b
}
function minus(a, b) {
return a - b
}
Let's start with the simplest function , Suppose you want to test the function , What we're testing , It's his function , Two parameters are passed in when the function is called , Return the result as their added value . For example, incoming add(1, 1), We expect the result to be the result of the addition 2
Write code like this expect(add(1, 1)) === 2, If we encapsulate it more semantically ,expect(add(1, 1)).toBe(2) For our expected test function , To implement this function :
function expect(result) {
return {
toBe(actual) {
if (result !== actual) {
throw new Error(` Not equal to the expected result . The actual result :${actual}, Expected results :${result}`)
}
},
}
}
Use :
expect(add(1, 1)).toBe(2)
expect(minus(2, 1)).toBe(1)
expect(add(2, 2)).toBe(3) // Error: Not equal to the expected result . The actual result :3, Expected results :4
If the test doesn't pass, we'll let it throw an error
It's a bit confusing to write like this , Because at first glance, I don't know what I'm testing , therefore , Let's encapsulate one more function , Represents what tests we're doing
// desc Used to describe test behavior , fn The function that we perform the test for
function test(desc, fn) {
try {
fn()
console.log(`${desc} Pass the test `)
} catch (e) {
console.log(`${desc} The test failed ${e}`)
}
}
test(' Addition test ', () => {
expect(add(1, 1)).toBe(2)
})
test(' Subtraction test ', () => {
expect(minus(2, 1)).toBe(1)
})
Execute code , Output
The addition test passed the test
The subtraction test passed the test
This is the simplest test we've ever done , Because our function is relatively simple , So you may feel like you're doing a lot of work .
From the perspective of process , A simple test process ( Input —— Expected output —— The verification results ) as follows :
- Introduce the function to be tested
- Pass in the test function execution result
- Define the expected output
- Check that the function returns the expected output
Initialization environment
Create an empty folder and enter initialization
npm init -y
install jest
yarn add --dev jest
stay package.json Configure script commands
{
"scripts": {
"test": "jest"
}
}
New file babel.config.js, Install dependencies and configure
yarn add --dev babel-jest @babel/core @babel/preset-env
// babel.config.js
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
],
}
Next, we'll take each of our introductions example.js It's code , Until then *.test.js The file import example.js And test .
matcher Matchers
Use Matchers Allows you to test your code in various ways
Just like the code we wrote above ,jest Provides test,expect,toBe Method
example.js
export const multiply = (a, b) => {
return a * b
}
New file matcher.test.js
import { multiply } from './example'
test(' Test multiplication ', () => {
expect(multiply(4, 2)).toBe(8)
})
toBe Use Object.is To test for exact equality . If you want to check the value of an object , Use toEqual Instead of .
matcher.test.js
test(' Test whether the objects are equal ', () => {
const data = { name: 'jacky' }
data['age'] = 23
expect(data).toEqual({ name: 'jacky', age: 23 })
})
function yarn test, You can see that the console shows two tests passed ; If not through the console, it will also prompt which test case is wrong .

When you run the command ,jest Will automatically search for items with .test.js Postfix file , And run the test .
Of course , There are other matchers
test(' matcher ', () => {
const n = null
const isTrue = true
const value = 3
expect(n).toBeNull() // Only match null
expect(n).toBeDefined() // Only match undefined
expect(n).not.toBeUndefined() // And toBeUndefined contrary
expect(isTrue).toBeTruthy() // Match any if The statement is true
expect(value).toBeGreaterThan(2)
expect(value).toBeLessThan(5)
expect(value).toBeLessThanOrEqual(3)
})
Test asynchronous code
example.js
import axios from 'axios'
export const fetchData = () => {
return axios.get('https://jsonplaceholder.typicode.com/todos/1')
}
async.test.js
import { fetchData } from './example'
test(' test async await', async () => {
const data = await fetchData()
expect(data.data.id).toBe(1) // request api Back to id Value is not 1
})
test(' test Promise', () => {
return fetchData().then(data => {
expect(data.data.id).toBe(1)
})
})
mock function
If we have a lot of asynchronous request functions to test , Will slow the test , Then the above method of using real interface request is not suitable ; Or you want to capture calls to functions , It's time to Mock, It can do :
- Capture calls to functions , as well as this And call order
- It allows you to configure the return value when testing
- Change the internal implementation of the function
mock.test.js
import axios from 'axios'
import { fetchData } from './example'
jest.mock('axios') // Automatic simulation axios modular .
test(' Test request interface ', async () => {
axios.get.mockResolvedValue({ data: 'hello' }) // Return false data { data: 'hello' } Used for testing
await fetchData().then(data => {
expect(data).toEqual({ data: 'hello' })
})
})
Maybe you think , What's the use of this test , It's all fake data , As the front end, this side , We just need to make sure that the function is called and returns the result normally ; Of course, this example is relatively simple , When it comes to complex interface testing, there are other things API You can use . Other in-depth and specific interface request testing is what the back-end needs to do .
stay example.js Add a function
// To execute this function, you need to pass in a callback function , Then the callback function passes a parameter and executes
export function runFn(fn) {
fn(' The function executes ')
}
If this is a complex function , We don't want to test just let it execute , You can use it mock To test
test(' Function called ', () => {
const fn = jest.fn()
runFn(fn) // Call function
expect(fn).toHaveBeenCalled() // expect fn Is called the
expect(fn).toHaveBeenLastCalledWith(' The function executes ') // function fn Whether the parameters at the time of being called conform to
})
Besides , You can also customize the return parameters , The number of times the test was executed and so on , Check official documents for details .
Snapshot
Whenever we want to make sure UI( finger html css etc. ) Or when the configuration file doesn't change , You can use a snapshot to test .
Here's just an example of a configuration file
example.test.js
export function getConfig() {
return {
port: 8080,
url: 'http://localhost:3000',
}
}
snapshot.test.js
import { getConfig } from './example'
test(' Test the configuration file for changes ', () => {
expect(getConfig()).toMatchSnapshot()
})
function npm run test, First run , You will find the location of the file and create a folder __snapshots__, There's a snapshot.test.js.snap file , It's about :
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[` Test the configuration file for changes 1`] = `
Object {
"port": 8080,
"url": "http://localhost:3000",
}
`;
It contains our configuration information , If you accidentally change the configuration information , An error will be reported during the execution of the test , Because it doesn't match the content of this document ; Then you will know that you should not change the position
For example, change the port to 8081, Run again npm run test

But if it really needs to be changed , Then follow the information in the prompt , Update snapshot , perform :
npm test -- -u
After execution, you will see :
Snapshots: 1 updated, 1 total
If you run the test again this time, there will be no error , The configuration information is updated .
Timers Mocks
When you want to test a function with a timer , Like before 5 What task does the second perform , It's not very convenient for us , To really wait for it to take a certain amount of time to execute , This is a timer function that can simulate .
example.js
export function timerGame(callback) {
console.log('Ready....go!')
setTimeout(() => {
console.log("Time's up -- stop!")
callback && callback()
}, 3000)
}
import { timerGame } from './example'
jest.useFakeTimers()
test(' After a certain period of time to test the function ', () => {
const fn = jest.fn()
timerGame(fn)
jest.advanceTimersByTime(3000) // Three seconds ahead of time
expect(fn).toBeCalled() // Function called
expect(fn).toHaveBeenCalledTimes(1) // The function is executed 1 Time
})
Test coverage
Add a script command , Output test coverage information as a report
"coverage": "jest --coverage"
Execute the command 
You can see the report generated on the terminal , The code test coverage for the above example is 100%, This is because the test function is relatively simple , So it's easy 100%.
At the same time, the project root directory also successfully created a folder coverage, Inside lcov-report Create... In the catalog HTML file , You can view it through the browser .
as follows :
TDD And BDD
Now the most popular unit testing styles are TDD( Test-driven development ) and BDD( Behavior driven development ) Two kinds of . The obvious difference is that one is to write test cases first and then write code , One is to write code first and then write test cases .
TDD
TDD The development process is usually to write test cases first , Then write the code .
For example, write a Todolist, Let's first think about its functions and points for attention , Each function has its own test case , Write test cases , It's not going to work until you write the code ; And then when you're done, you start writing code , Make the test case pass the test , Go on and repeat the steps , Complete development . The quality and maintainability of the code written in this way will also be better .
therefore TDD It's usually used as a unit test , That is, the function of a single component is tested more perfectly , But when several components add up to test this, it may not be perfect .
Function library is widely used in unit testing , Each function can be tested individually and carefully .
Unit testing is relatively independent , But being too independent can also hide some potential problems that can't be detected , This is the time for integration testing .
BDD
BDD The development process is usually to write code first , Write test cases again .
This is more in line with our previous development model , however BDD More attention is paid to whether the overall behavior is in line with expectations , Generally combined with integration testing , In other words, the whole business of many components is tested .
Integration testing focuses on the results , Not the code , More suitable for business development .
Conclusion
Both test styles have their own advantages and disadvantages , Here are just a few examples ; The whole thing is simple to understand , Front end testing allows us to write better code , Reduce bug The birth of ; and , In terms of maintaining some old projects , There are also advantages , For example, when you change the code of an old project , You may be afraid to affect other components or functions, but you don't know , But if there's a test , After running all the test code directly, you can know whether other function code is affected ; And if the old project doesn't write units / Integration testing , For better maintenance , You can also add test code .
If you want to learn more about Jest The content of the test , Of course, go straight Jest Official website I saw , Then in the actual development and application will gradually realize the benefits it brings .
- ps: Personal technology blog Github Warehouse , Welcome... If you think it's good star, Give me a little encouragement to keep writing ~







