jest是Facebook出品的一个JavaScript开源测试框架。内置了零配置、自带断言、测试覆盖率工具等,实现了开箱即用。
npm i -D jest
npm i -D @types/jest
初始化jest默认文件
$ npx jest --init
npx: 332 安装成功,用时 29.723 秒
The following questions will help Jest to create a suitable configuration for your project
√ Would you like to use Jest when running "test" script in "package.json"? ... yes
√ Would you like to use Typescript for the configuration file? ... no
√ Choose the test environment that will be used for testing » jsdom (browser-like)
√ Do you want Jest to add coverage reports? ... yes
√ Which provider should be used to instrument code for coverage? » babel
√ Automatically clear mock calls, instances and results before every test? ... yes
生成jest.config.js配置文件,并有jest的所有注释的配置文件
/*
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\Alvin\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls, instances and results before every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: true,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// Indicates which provider should be used to instrument code for coverage
// coverageProvider: "babel",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage 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 array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
// moduleNameMapper: {},
// An array of regexp pattern strings, matched against all module 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 to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
// roots: [
// ""
// ],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "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 against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect 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: "jest-circus/runner",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake 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 against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\",
// "\\.pnp\\.[^\\\\]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
监视文件的更改并在任何更改时重新运行所有测试
jest --watchAll
需要git支持
jest --watch
安装解析依赖
npm i -D babel-jest @babel/core @babel/preset-env
配置babel.config.js
module.exports = {
presets: [['@babel/preset-env',{
targets: {
node: 'current' // node环境的解释
}
}]]
}
test函数的别名:it(name, fn, timeout)
it('匹配器', () => {
expect(2 + 2).tobe(4);
expect({name: 'alvin'}).toEqual({name: 'alvin'});
expect('Christoph').toMatch(/stop/);
expect(4).toBeGreaterThan(3);
expect(3).toBeLessThan(4);
})
describe创建一个将几个相关测试组合在一起的块。
afterALl(fn, timeout)
afterEach(fn, timeout)
beforeAll(fn, timeout)
beforeEach(fn, timeout)
Jest对象自动位于每个测试文件中的范围内。Jest对象中的方法有助于创建模拟,并让你控制Jest的整体行为。也可以通过import {jest} form '@jest/globals’导入。详细参考: https://jestjs.io/docs/jest-objest
jest对象中有许多的功能函数,例如:模拟定时器:jest.useFakeTimers()
运行单个的测试文件
npm run test -- expect.spec.js
官方使用文档:https://jestjs.io/docs/using-matchers
test('null', () => {
const n = null;
expect(n).toBeNull();
expect(n).toBeDefined();
expect(n).not.toBeUndefined();
expect(n).not.toBeTruthy();
expect(n).toBeFalsy();
});
test('zero', () => {
const z = 0;
expect(z).not.toBeNull();
expect(z).toBeDefined();
expect(z).not.toBeUndefined();
expect(z).not.toBeTruthy();
expect(z).toBeFalsy();
});
test('two plus two', () => {
const value = 2 + 2;
expect(value).toBeGreaterThan(3);
expect(value).toBeGreaterThanOrEqual(3.5);
expect(value).toBeLessThan(5);
expect(value).toBeLessThanOrEqual(4.5);
// toBe and toEqual are equivalent for numbers
expect(value).toBe(4);
expect(value).toEqual(4);
});
test('adding floating point numbers', () => {
const value = 0.1 + 0.2;
//expect(value).toBe(0.3); This won't work because of rounding error
expect(value).toBeCloseTo(0.3); // This works.
});
test('there is no I in team', () => {
expect('team').not.toMatch(/I/);
});
test('but there is a "stop" in Christoph', () => {
expect('Christoph').toMatch(/stop/);
});
const shoppingList = [
'diapers',
'kleenex',
'trash bags',
'paper towels',
'milk',
];
test('the shopping list has milk on it', () => {
expect(shoppingList).toContain('milk');
expect(new Set(shoppingList)).toContain('milk');
});
function compileAndroidCode() {
throw new Error('you are using the wrong JDK');
}
test('compiling android goes as expected', () => {
expect(() => compileAndroidCode()).toThrow();
expect(() => compileAndroidCode()).toThrow(Error);
// You can also use the exact error message or a regexp
expect(() => compileAndroidCode()).toThrow('you are using the wrong JDK');
expect(() => compileAndroidCode()).toThrow(/JDK/);
});
function getData(callback){
setTimeout(() => {
callback({foo: 'bar'})
}, 2000)
}
it("异步测试", (done) => {
getData(data =>{
done()
expect(data).toEqual({foo: 'bar'})
})
})
function getData(data){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({foo: 'bar'})
}, 2000)
})
}
it("异步promise测试", (done) => {
getData().then(data => {
done()
expect(data).toEqual({foo: 'bar'})
})
})
it("异步promise直接return", () => {
return getData().then(data => {
expect(data).toEqual({foo: 'bar'})
})
})
it("异步promise 使用.resolve、.rejects", () => {
return expect(getData()).resolves.toEqual({foo: 'bar'})
})
function getData(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({foo: 'bar'})
}, 2000)
})
}
it('async和await方式测试异步代码', async () => {
const data = await getData();
expect(data).toEqual({foo: 'bar'})
})
it('async和await方式测试异步代码', async () => {
try{
await getData();
}catch(err) {
expect(err).toMatch('hello')
}
})
test('the data is peanut butter', async () => {
await expect(fetchData()).resolves.toBe('peanut butter');
});
test('the fetch fails with an error', async () => {
await expect(fetchData()).rejects.toMatch('error');
});
function getData(){
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({foo: 'bar'})
}, 2000)
})
}
// mock 定时器
jest.useFakeTimers()
it('timer mock', () => {
expect.assertions(1);
getData().then(data => {
expect(data).toEqual({foo: 'bar'})
})
jest.runAllTimers()
})
function forEach(items, callback){
for(let index = 0; index < items.length; index++){
callback(items[index], index)
}
}
it('Mock Function', () => {
const items = [1, 2, 3];
const mockFn = jest.fn((value, index) => {
return value + 1;
})
//设置所有的返回值都是默认值
// mockFn.mockReturnValue(123)
//mockFn.mockReturnValueOnce(123)
//mockFn.mockReturnValueOnce(456)
forEach(items, mockFn)
expect(mockFn.mock.calls.length).toBe(items.length)
expect(mockFn.mock.calls[0][0]).toBe(1)
expect(mockFn.mock.calls[0][1]).toBe(0)
})
user.js
import axios from 'axios';
export const getAllUsers = () => {
return axios.get('/user.json').then(resp => resp.data)
}
mock-function
import { getAllUsers } from "./user";
import axios from "axios";
jest.mock('axios');
it('fetch Users', async () => {
const users = [{name: 'bob'}]
const resp = {data: users}
axios.get.mockResolvedValue(resp)
const data = await getAllUsers()
expect(data).toEqual(users)
})
// The mock function was called at least once
// expect(mockFunc).toHaveBeenCalled();
// The mock function was called at least once with the specified args
// expect(mockFunc).toHaveBeenCalledWith(arg1, arg2);
// The last call to the mock function was called with the specified args
// expect(mockFunc).toHaveBeenLastCalledWith(arg1, arg2);
// All calls and the name of the mock is written as a snapshot
// expect(mockFunc).toMatchSnapshot();
./foo.js
export default function(){
console.log('foo')
}
import foo from './foo'
jest.mock('./foo')
foo.mockImplementations(() => {
return 123;
})
it('mock Implementations', () => {
expect(foo()).toBe(123)
})
// 运行每个测试用例之前先执行它,describe作用域
beforEach(() => {
console.log('beforeEach')
})
// 每个测试用例执行结束后执行,describe作用域
afterEach(() => {
console.log('afterEach')
})
// 在所有的测试用例之前执行一次
beforAll(() => {
console.log('beforAll')
})
// 在所有的测试用例执行完后执行一次
afterAll(() => {
console.log('afterAll')
})
function renderHtml(){
const div = document.createElement('div');
div.innerHTML = `Hello World
`
document.body.appendChild(div)
}
it('Dom Test', () => {
renderHtml()
expect(document.querySelector('h1').innerHTML).toBe('Hello World')
})
import Vue from 'vue/dist/vue';
function renderVueComponent(){
document.body.innerHTML = ``
new Vue({
template: `{{message}}
`,
data(){
return {
message: 'Hello World'
}
}
}).$mount('#app')
}
it('测试Vue组件', () => {
renderVueComponent()
console.log(document.body.innerHTML)
expect(document.body.innerHTML).toMatch(/Hello World/);
})
it('快照测试', () => {
renderVueComponent()
// 第一次运行的时候会生成快照文件字符串,下一次运行的时候会和快照文件进行比对
expect(document.body.innerHTML).toMatchSnapshot()
})
更新快照的命令
npx jest --updateSnapshot