For those of you with a bit of development experience, you will always experience similar problems in the development process:

  • Squatting in front of your computer for hours or even longer to test your app before it goes live can be tedious and painful.

  • As the complexity of your code reaches a certain level, and you have more than one maintainer, you should begin to notice that you are becoming more cautious about developing new features or fixing bugs. Even if the code looks fine, you will still be wondering: Will this Feature cause other bugs? Will this Fix introduce any other “features”?

  • When you want to refactor code in a project, you spend a lot of time doing regression testing.

All of these problems are caused by the basic manual testing method that most developers use, and the fundamental solution is to introduce automated testing solutions.

Test flow

Writing automated test code is often a part of the actual development process that developers don’t like very much. Most of the time, the front-end developer, after developing a feature, just opens the browser and clicks manually to see if it works, and then does little to manage that block of code.

There are two main reasons for this:

  • One is that business is busy and there is no time to write tests.

  • The other is not knowing how to write tests.

But these issues shouldn’t stand in the way of mastering front-end automation. In addition, mastering the front end automation test solution will be beneficial to both the development of large projects and the promotion and salary.

When it comes to testing, even the simplest block of code can be overwhelming for beginners. The most frequently asked question is “How do I know what to test?” . If you’re writing a Web application, the way you test user interaction on a per-page basis is a good place to start. But Web applications are also code units composed of many functions and modules that need to be tested. There are usually two situations:

  • The legacy code you inherited didn’t write test cases

  • You have to implement a new feature from scratch

What to do? In both cases, you can write tests as part of your code. The code I’m talking about checks to see if a given function produces the desired output. A typical test flow is as follows:

1. Introduce the function to be tested

2. Give the function an input

3. Define the expected output

4. Check whether the function returns the expected output

That’s all. It’s not so scary to look at testing this way: input — expected output — verified results.

A test case

Here’s an example:

// math.js
functionadd (a, b) {
  return a + b
}​

functionsubtract (x, y) {
  return x - y
}​

module.exports= {
  add,
  subtract
}
Copy the code

How to guarantee the correctness of the above code?

Write a test code below:

// test.js const {add, subtract} =require('./math') const result =add(1,2) const expected =3 if (result! == expected) {thrownewError(' 1 + 2 should be equal to ${expected}, Subtract (2,1) const expected2 =1 if (result2! == Expected2) {thrownewError(' 2-1 should be equal to ${expected2}, but the result is ${result2} ')}Copy the code

After executing node test.js on the command line, you will see an error message:

Error: 1 + 2 should be equal to 3, but it is 2Copy the code

Testing your code is a handy way to help validate your code.

Encapsulate test tool functions

The previous example of the test code is too cumbersome, you can think of a more simple package, such as the following:

Expect (add (1, 2)). Place (3) expect (subtract (2, 1)). The place (1)Copy the code

The above test code is comfortable speaking in natural language.

Implement expect methods:

// test.js const { add, Subtract} =require('./math') expect(add(1,2)).tobe (3) expect(subtract(2,1)).tobe (1) functionexpect (result) {return { toBe (actual) { if (result ! == actual) {thrownewError(${actual}, ${result} ')}}}}Copy the code

Add error message:

/ / test. Js const {add, subtract} = the require ('. / math) test (' test addition, () = > {expect (add (1, 2)). The place (3)}) test (' test subtraction, Subtract () => {expect(subtract(2,1)).tobe (1)}) functiontest (description, Callback) {try {callback() console.log(' ${description} passed the test ')} catch (err) {console.error(' ${description} failed the test: ${err}`) } } functionexpect (result) { return { toBe (actual) { if (result ! == actual) {thrownewError(${actual}, ${result} ')}}}}Copy the code

Jest test framework application

Jest: Jestjs. IO /zh-Hans/ is a JavaScript open source testing framework produced by Facebook.

Compared with other testing frameworks, one of the major features is the built-in common testing tools, such as zero configuration, built-in assertions, test coverage tools and other functions, to achieve out-of-the-box.

Jest works for, but is not limited to, projects that use Babel, TypeScript, Node, React, Angular, Vue, and more.

Jest main features:

  • Zero configuration

  • Bring their own assertion

  • As a front-end testing framework, Jest can use its unique snapshot testing function to automatically test common front-end frameworks such as React by comparing the snapshot files generated by UI code.

  • Jest’s test cases are executed in parallel, and only the tests for the changed files are executed, increasing testing speed.

  • Test coverage

  • The Mock simulation

Play Jest quickly

Install Jest into the project:

npm install --save-dev jest //math.js functionadd (a, b) { return a * b } functionsubtract (x, y) { return x - y } module.exports= { add, subtract } //test.js ==> math.test.js const { add, Subtract} = the require ('. / math) test (' test addition, () = > {expect (add (1, 2)). The place (3)}) test (' test subtraction, () = > {expect (subtract (2, 1)). The place (1)}) / / package. Json {" scripts ": {" test", "jest"}}Copy the code

The jest command runs all files in the project that end with.test.js

Finally, run the test command:

npm run test
Copy the code

Resolution:

  • Jest finds all files in the project that end in.test.js and runs it

  • Jest provides global functions such as Test and Expect to the test file, so they can be used directly in the test file

  • Jest provides good logging output for test results

Fix jest code prompts in vscode

npm i -D @types/jest
Copy the code

Note: @types/jest must be installed in the root directory of the project and opened in vscode as a root directory otherwise it will not take effect. Or as long as vscode opens a project with @types/jest in its root directory.

The configuration file

npx jest--init
Copy the code

Configuration file generation options:

Whether to use TS;

Which test environment to use;

Collect test coverage reports using JEST;

Which engine detection coverage to use: V8 is experimental and recommended after Node V14, Babel is mature

Whether the mock instance is automatically cleared each time the test is performed;

For detailed configuration information, see jestjs. IO /docs/ zh-han… .

//jest.config.js
/*
 * For a detailedexplanation regarding each configuration property, visit:
 *https://jestjs.io/docs/en/configuration.html
 */​

module.exports= {
  // 自动 mock 所有导入的外部模块
  // automock: false,

​  // 在指定次数失败后停止运行测试
  // bail: 0,​

  // The directory where Jest should store its cached dependencyinformation
  // cacheDirectory:"/private/var/folders/5h/_98rffpj1z95b_0dm76lvzm40000gn/T/jest_dx",​

  // 在每个测试之间自动清除 mock 调用和实例
  clearMocks:true,

​  // 是否收集测试覆盖率信息
  // collectCoverage: false,​

  // 一个 glob 模式数组,指示应该为其收集覆盖率信息的一组文件
  // collectCoverageFrom: undefined,

​  // 测试覆盖率报错文件输出的目录
  coverageDirectory:"coverage",

​  // 忽略测试覆盖率统计
  // coveragePathIgnorePatterns: [
  //  "/node_modules/"
  // ],​

  // 指示应该使用哪个提供程序检测代码的覆盖率,默认是 babel,可选 v8,但是 v8 不太稳定,建议Node 14 以上版本使用
  // coverageProvider: "babel",​

  // A list of reporter names that Jest uses when writingcoverage reports
  // coverageReporters: [
  //   "json",
  //   "text",
  //   "lcov",
  //   "clover"
  // ],​

  // An object that configures minimum threshold enforcement forcoverage results
  // coverageThreshold: undefined,

​  // A path to a custom dependency extractor
  // dependencyExtractor: undefined,

​  // Make calling deprecated APIs throw helpful error messages
  // errorOnDeprecated: false,

​  // Force coverage collection from ignored files using an arrayof glob patterns
  // forceCoverageMatch: [],​

  // A path to a module which exports an async function that istriggered once before all test suites
  // globalSetup: undefined,​

  // A path to a module which exports an async function that istriggered once after all test suites
  // globalTeardown: undefined,

​  // A set of global variables that need to be available in alltest environments
  // globals: {},

​  // The maximum amount of workers used to run your tests. Canbe specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPUamount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2workers.
  // maxWorkers: "50%",

​  // An array of directory names to be searched recursively upfrom the requiring module's location
  // moduleDirectories: [
  //  "node_modules"
  // ],

​  // An array of file extensions your modules use
  // moduleFileExtensions: [
  //   "js",
  //   "json",
  //   "jsx",
  //   "ts",
  //   "tsx",
  //   "node"
  // ],

​  // A map from regular expressions to module names or to arraysof module names that allow to stub out resources with a single module
  // moduleNameMapper: {},​

  // An array of regexp pattern strings, matched against allmodule paths before considered 'visible' to the module loader
  // modulePathIgnorePatterns: [],

​  // Activates notifications for test results
  // notify: false,

​  // An enum that specifies notification mode. Requires {notify: true }
  // notifyMode: "failure-change",

​  // A preset that is used as a base for Jest's configuration
  // preset: undefined,​

  // Run tests from one or more projects
  // projects: undefined,​

  // Use this configuration option to add custom reporters toJest
  // reporters: undefined,​

  // Automatically reset mock state between every test
  // resetMocks: false,

​  // Reset the module registry before running each individualtest
  // resetModules: false,

​  // A path to a custom resolver
  // resolver: undefined,

​  // Automatically restore mock state between every test
  // restoreMocks: false,

​  // The root directory that Jest should scan for tests andmodules within
  // rootDir: undefined,​

  // A list of paths to directories that Jest should use tosearch for files in
  // roots: [
  //  "<rootDir>"
  // ],​

  // Allows you to use a custom runner instead of Jest's defaulttest runner
  // runner: "jest-runner",​

  // The paths to modules that run some code to configure or setup the testing environment before each test
  // setupFiles: [],​

  // A list of paths to modules that run some code to configureor set up the testing framework before each test
  // setupFilesAfterEnv: [],

​  // The number of seconds after which a test is considered asslow and reported as such in the results.
  // slowTestThreshold: 5,​

  // A list of paths to snapshot serializer modules Jest shoulduse for snapshot testing
  // snapshotSerializers: [],​

  // The test environment that will be used for testing
  // testEnvironment: "jest-environment-jsdom",

​  // Options that will be passed to the testEnvironment
  // testEnvironmentOptions: {},​

  // Adds a location field to test results
  // testLocationInResults: false,

​  // The glob patterns Jest uses to detect test files
  // testMatch: [
  //  "**/__tests__/**/*.[jt]s?(x)",
  //  "**/?(*.)+(spec|test).[tj]s?(x)"
  // ],​

  // An array of regexp pattern strings that are matched againstall test paths, matched tests are skipped
  // testPathIgnorePatterns: [
  //  "/node_modules/"
  // ],​

  // The regexp pattern or array of patterns that Jest uses todetect test files
  // testRegex: [],

​  // This option allows the use of a custom results processor
  // testResultsProcessor: undefined,

​  // This option allows use of a custom test runner
  // testRunner: "jasmine2",​

  // This option sets the URL for the jsdom environment. It isreflected in properties such as location.href
  // testURL: "http://localhost",​

  // Setting this value to "fake" allows the use offake timers for functions such as "setTimeout"
  // timers: "real",​

  // A map from regular expressions to paths to transformers
  // transform: undefined,​

  // An array of regexp pattern strings that are matched againstall source file paths, matched files will skip transformation
  // transformIgnorePatterns: [
  //  "/node_modules/",
  //  "\\.pnp\\.[^\\/]+$"
  // ],

​  // An array of regexp pattern strings that are matched againstall modules before the module loader will automatically return a mock for them
  // unmockedModulePathPatterns: undefined,

​  // Indicates whether each individual test should be reportedduring the run
  // verbose: undefined,​

  // An array of regexp patterns that are matched against allsource file paths before re-running tests in watch mode
  // watchPathIgnorePatterns: [],

​  // Whether to use watchman for file crawling
  // watchman: true,
};
Copy the code

Jest CLI Options

Reference: jestjs. IO/useful – Hans/doc…

Specifies the test file to run

  "scripts":{
    "test":"jest ./math.test.js"
  },
Copy the code

Jest monitoring mode

–watchAll option: Monitors file changes and rerun all tests on any changes.

 "scripts": {
   "test": "jest --watchAll"
  },
Copy the code

Jest API

In the test file, Jest puts all of these methods and objects into the global environment. They can be used without requiring or importing anything. However, if you prefer explicit imports, you can:

import { describe, expect, test } from'@jest/globals'
Copy the code

The Test function

Test function alias: it(name, fn, timeout).

  • test(name,fn, timeout)

  • test.concurrent(name, fn, timeout)

  • test.concurrent.each(table)(name, fn, timeout)

  • test.concurrent.only.each(table)(name, fn)

  • test.concurrent.skip.each(table)(name, fn)

  • test.each(table)(name, fn, timeout)

  • test.only(name, fn, timeout)

Run only the current test case

  • test.only.each(table)(name, fn)

  • test.skip(name,fn)

  • test.skip.each(table)(name, fn)

  • test.todo(name)

Create global-api.test.js test file. Note that there must be a test case in the test file. If not, an error will be reported.

test('should ', () => { console.log('test--api') }) test('should ', () => { console.log('test--api') }) test('should1 ', () = > {the console. The log (' test - api1 ')}) / / the above two don't run the test. The only (' should2 '() = > {the console. The log (' test - api2')})Copy the code

Expect verifier

When writing tests, you often need to check that values meet certain conditions. Expect gives us access to many matchers to validate different things.

test('two plus two is four', () => {
  expect(2+2).toBe(6)
  expect({ name:'jack' }).toEqual({ name:'jack' })
  expect('Christoph').toMatch(/stop/)
  expect(4).toBeGreaterThan(3)
  expect(4).toBeLessThan(5)
})
Copy the code

Jestjs. IO/zh-hans /doc…

The describe function

Describe creates a block that groups several related tests together.

const myBeverage = {
  delicious:true,
  sour:false,
};

​describe('my beverage', () => {
  test('is delicious', () => {
    expect(myBeverage.delicious).toBeTruthy();
  });​

  test('is not sour', () => {
    expect(myBeverage.sour).toBeFalsy();
  });
});
Copy the code

The most intuitive thing about grouping is that in the test prompt, there is a more friendly message output.

  • describe(name,fn)

  • describe.each(table)(name, fn, timeout)

  • describe.only(name,fn)

  • describe.only.each(table)(name, fn)

  • describe.skip(name,fn)

  • describe.skip.each(table)(name, fn)

To be continued.