Author: Lin Lihuan, member of Meituan Dianping ordering team.
Overview of front-end test tools
Front-end testing tools are as complex as front-end frameworks. Common testing tools can be roughly divided into testing frameworks, assertion libraries, test coverage tools and so on. Before we begin, let’s take a look at them:
The test framework
The purpose of a testing framework is to provide some convenient syntax for describing test cases and grouping them.
Testing frameworks fall into two categories: I understand that the differences between TDD (test Driven development) and BDD (behavior driven Development) are mainly grammatical differences, where BDD provides a more readable use case syntax, See The Difference Between TDD and BDD for details.
Common testing frameworks are Jasmine, Mocha, and Jest, which I’ll cover in this article.
Assertions library
The assertion library mainly provides semantic methods for making various judgments about the values being tested. These semantic methods return the results of the test, either success or failure. Common assertion libraries are should.js, Chai. Js, etc.
Test coverage tool
Use it to count the test cases against the code and generate a report such as Istanbul.
Jest
Why Jest
Jest is a testing framework produced by Facebook. Compared with other testing frameworks, one of the major features is that it has built-in common testing tools, such as built-in assertion and test coverage tools, which are implemented out of the box.
As a front-end testing framework, Jest can use its unique snapshot testing function to automatically test common frameworks such as React by comparing the snapshot files generated by UI code.
In addition, Jest’s test cases are executed in parallel and only the tests for the changed files are executed, increasing testing speed. At present, the number of its stars on Github has exceeded 10,000; In addition to Facebook, other companies in the industry are starting to turn to Jest from other testing frameworks, such as Airbnb, and we believe that Jest will continue to grow rapidly in the future.
The installation
Jest can be installed using NPM or YARN. Using NPM as an example, you can use NPM install -g jest for global installation. You can also install the test script only partially and specify it in package.json:
{
"scripts": {
"test": "jest"}}Copy the code
Jest’s test script name is *.test.js. Whether Jest is run globally or through NPM test, it executes all *.test.js or *.spec.js files in the current directory to complete the test.
The basic use
Representation of use cases
Presentation test cases are the most basic API provided by a testing framework, and Jest uses Jasmine 2 internally for testing, so its use case syntax is the same as Jasmine. The test() function describes a test case, as a simple example:
// hello.js
module.exports = () => 'Hello world'
Copy the code
// hello.test.js
let hello = require('hello.js')
test('should get "Hello world"', () => {
expect(hello()).toBe('Hello world'// expect(hello()).tobe ()'Hello') // Test failed})Copy the code
ToBe (‘Hello world’) is an assertion (Jest calls it “matcher”, see documentation for more matcher). After writing the use case, run NPM test under the project directory and see the test result:
If the test fails, the failed assertion location is identified with the following result:
Pre-processing or post-processing of use cases
Sometimes we want to check the environment before the test starts or clean up after the test, which requires pre-processing or post-processing of the use case. Uniformly preprocess all use cases in the test file using the beforeAll() function; If you want to preprocess each use case before it starts, use the beforeEach() function. For post-processing, there are afterAll() and afterEach() functions.
If you just want to do the same pre-processing or post-processing for a few use cases, you can group them together first. The describe() function is used to represent a set of use cases, and the four handlers mentioned above are placed within the describe() processing callback to achieve pre-processing or post-processing of a set of use cases:
describe('test testObject', () => {beforeAll(() => {// preprocessing operation})test('is foo', () => {
expect(testObject.foo).toBeTruthy()
})
test('is not bar', () => {
expect(testObject. The bar). ToBeFalsy ()}) afterAll (() = > {/ / post-processing operation})})Copy the code
Testing asynchronous code
The key to testing asynchronous code is to tell the testing framework when the test is complete so that it can make assertions at the appropriate time. Jest also provides asynchronous test syntax for several common forms of asynchronous code. Jest will wait for the done callback to complete, and the test will end:
// asyncHello.js
module.exports = (name, cb) => setTimeout(() => cb(`Hello ${name}`), 1000)
Copy the code
// asyncHello.test.js
let asyncHello = require('asyncHello.js')
test('should get "Hello world"', (done) => {
asyncHello('world', (result) => {
expect(result).toBe('Hello world')
done()})})Copy the code
In addition, for asynchronous code controlled by Promise, assertions can be made directly in the THEN callback, as long as the Promise object is guaranteed to be returned in the use case:
// promiseHello.js
module.exports = (name) => {
return new Promise((resolve) => {
setTimeout(() => resolve(`Hello ${name}`), 1000)})}Copy the code
// promiseHello.test.js
let promiseHello = require('promiseHello.js')
it('should get "Hello world"', () => { expect.assertions(1); // Make sure at least one assertion is called, otherwise the test failsreturn promiseHello('world').then((data) => {
expect(data).toBe('Hello world')})})Copy the code
Jest also supports testing with async/await syntax, which requires no further operations, as long as an assertion is made after an await syntax.
Test coverage
Istanbul is a test coverage tool built into Jest. To start this, add the –coverage parameter directly to the command or configure it in more detail in the package.json file.
Running Istanbul will not only display test coverage on the terminal, it will also produce a coverage directory under the project with a test coverage report so that we can clearly see how the branch’s code is being tested. Take this example:
// branches.js
module.exports = (name) => {
if (name === 'Levon') {
return `Hello Levon`
} else {
return `Hello ${name}`}}Copy the code
// branches.test.js
let branches = require('.. /branches.js')
describe('Multiple branches test', () = > {test('should get Hello Levon', ()=> {
expect(branches('Levon')).toBe('Hello Levon')}); //test('should get Hello World', ()=> {
// expect(branches('World')).toBe('Hello World')
// });
})
Copy the code
Run jest –coverage to see the resulting report showing code coverage and untested lines:
If we remove the comments in branches. Test.js and run over all branches in the test object, the test coverage is 100% :
Used in front-end projects
Work with React and other frameworks
One of the main features of Jest for testing front-end frameworks is snapshot testing. The first time you run the snapshot test, the UI framework produces a readable snapshot, and the second time you run the test, you can determine whether the test passes by comparing the snapshot file to the snapshot generated by the new UI framework. React produces a snapshot using the following method:
import React from 'react';
import Link from '.. /Link.react';
import renderer from 'react-test-renderer';
it('renders correctly', () => {
const tree = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
).toJSON();
expect(tree).toMatchSnapshot();
});
Copy the code
Running the test, we can see that a snapshot file is generated as follows:
exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;
Copy the code
This readable snapshot file shows the DOM structure rendered by React in a readable form. Compared with the UI test of visual observation, the snapshot test is directly compared by Jest, which is faster. And because the DOM structure is directly exposed, it allows us to quickly and accurately find problems when examining snapshots.
In addition to React, the Jest documentation also provides guidelines for testing against other frameworks.
Seamless migration
If your project already uses another testing framework, such as Mocha, there is a third-party tool called Jest-Codemods that can automatically migrate use cases to JEST’s use cases, reducing migration costs.
Postscript: Is front-end automation testing worth it?
In recent years, the development of front-end engineering is surging, but the content of front-end automation testing seems to be paid little attention to. The state in which the code has been developed by the time a dedicated tester arrives to test it, although there will be dedicated testers to test it during a project iteration. In contrast, if we test during development (directly using TDD development mode, or writing use cases for existing modules), it has the following benefits:
- Ensure the integrity of code quality and functionality
- Improving development efficiency and testing during the development process allows us to find bugs in advance, so we can locate and fix them much faster than we can be called back to fix bugs after development
- To facilitate project maintenance, any subsequent code updates must also run through the test cases, even if refactoring or developer changes are required to ensure that the intended functionality is implemented
Of course, there are two sides to every coin, and while the benefits are obvious, not every project is worth introducing a testing framework because of the cost of maintaining test cases. For something with frequently changing requirements and low reusability, such as an active page, it really doesn’t pay to have development dedicated to writing test cases.
There are a few suitable scenarios for introducing tests:
- Projects that require long-term maintenance. They require tests to ensure code maintainability and functional stability
- A stable project or a stable part of a project. Writing test cases for them is low maintenance
- Parts that are reused, such as common components and library functions. Because of multiple reuse, more to ensure quality
The above is my shallow view of front-end testing, welcome to be corrected.
Ref
Talk about front-end automation testing