技術交流羣: fiora.suisuijiang.com/
原文連接: github.com/yinxin630/b…css
Jest 是一款簡單, 容易上手且功能十分強大的測試框架html
yarn add -D jest
node
建立 test
目錄, 添加 plus.spec.js
文件react
describe('example', () => {
it('should equal 2', () => {
expect(1 + 1).toBe(2);
});
});
複製代碼
執行 yarn jest
或者 yarn jest test/plus.spec.js
運行測試用例webpack
成功結果ios
失敗結果git
在根目錄建立 jest.config.js
配置文件github
module.exports = {
collectCoverage: true,
};
複製代碼
建立 plus.js
模塊web
module.exports = function plus(a, b) {
return a + b;
}
複製代碼
修改測試用例使用模塊typescript
const plus = require('../plus');
describe('example', () => {
it('should equal 2', () => {
expect(plus(1, 1)).toBe(2);
});
});
複製代碼
再次執行測試, 輸出覆蓋率以下
在瀏覽器中打開 coverage/lcov-report/index.html
能夠瀏覽覆蓋率結果頁面
修改 plus.ts
模塊, 添加更多分支
export default function plus(a: number, b: number) {
if (a + b > 100) {
return 0;
} else if (a + b < 0) {
return 0;
} else {
return a + b;
}
}
複製代碼
從新執行測試, 覆蓋率輸出結果
你能夠完善測試用例, 或者可能有些文件(譬如 config)和代碼分支並不須要測試, 能夠將其在測試覆蓋率結果中排除, 參考以下配置
在 jest.config.js
中添加
collectCoverageFrom: [
'**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/[directory path]/**',
],
複製代碼
以 !
開頭的表示忽略與其匹配的文件
在該文件頂部添加 /* istanbul ignore file */
在該函數, 分支邏輯或者代碼行的上一行添加 /* istanbul ignore next */
執行 yarn add -D typescript ts-jest @types/jest
安裝 typescript 和聲明 並在 jest.config.js
中添加 preset: 'ts-jest'
將 plus.js
重命名爲 plus.ts
export default function plus(a: number, b: number) {
return a + b;
}
複製代碼
一樣的, 將 plus.spec.js
重命名爲 plus.spec.ts
import plus from '../plus'
describe('example', () => {
it('should equal 2', () => {
expect(plus(1, 1)).toBe(2);
});
});
複製代碼
執行測試, 結果和以前一致
有時你可能會但願不校驗 ts 類型, 僅執行代碼測試, 好比須要在 CI 中將類型校驗和單元測試分爲兩個任務 在 jest.config.js
中添加以下內容
globals: {
'ts-jest': {
isolatedModules: true,
},
}
複製代碼
安裝 react 依賴 yarn add react react-dom
和聲明 yarn add -D @types/react
安裝 react 測試庫 yarn add -D @testing-library/react @testing-library/jest-dom
添加 typescript 配置文件 tsconfig.json
{
"compilerOptions": {
"target": "es2018",
"strict": true,
"moduleResolution": "node",
"jsx": "react",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"lib": ["es2015", "es2016", "es2017", "dom"]
},
"exclude": ["node_modules"]
}
複製代碼
新增測試組件 Title.tsx
import React from 'react';
function Title() {
return (
<h1>Title</h1>
);
}
export default Title;
複製代碼
新增測試用例 test/Title.spec.tsx
/** * @jest-environment jsdom */
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Title from '../Title';
describe('Title', () => {
it('should render without error', () => {
const { getByText } = render(<Title />);
const $title = getByText('Title');
expect($title).toBeInTheDocument();
});
});
複製代碼
執行 yarn jest test/Title.spec.ts
查看結果
react 組件有時引用一些靜態資源, 譬如圖片或者 css 樣式表, webpack 會正確的處理這些資源, 可是對 Jest 來說, 這些資源是沒法識別的
建立 Title.less
樣式表
h1 {
color: red;
}
複製代碼
修改 Ttitle.tsx
, 添加樣式引用 import './Title.less';
執行測試會報錯
咱們須要配置 transform 對其處理
在根目錄建立 jest.transformer.js
const path = require('path');
module.exports = {
process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
},
};
複製代碼
這裏是將資源文件名做爲模塊導出內容
修改 jest.config.js
添加以下配置
transform: {
'\\.(less)$': '<rootDir>/jest.transformer.js', // 正則匹配, 處理 less 樣式
},
複製代碼
而後從新執行測試就能夠了
若是你使用了相似 linaria 這種 css in js 方案, 其中的 css 樣式模板字符串是不支持運行時編譯的
修改 Title.tsx
import React from 'react';
import { css } from 'linaria';
const title = css` color: red; `;
function Title() {
return <h1 className={title}>Title</h1>;
}
export default Title;
複製代碼
運行測試會報錯
linaria 是經過 babel 插件將其預編譯爲 class 名的, 這裏能夠 mock 一下 css
函數, 返回一個隨機值做爲 class 名
在根目錄建立 jest.setup.js
jest.mock('linaria', () => ({
css: jest.fn(() => Math.floor(Math.random() * (10 ** 9)).toString(36)),
}));
複製代碼
修改 jest.config.js
添加以下配置
setupFilesAfterEnv: ['./jest.setup.js'],
複製代碼
從新執行測試就能夠了
新增 Count.tsx
組件
import React, { useState } from 'react';
function Count() {
const [count, updateCount] = useState(0);
return (
<div> <span data-testid="count">{count}</span> <button data-testid="button" onClick={() => updateCount(count + 1)}> +1 </button> </div>
);
}
export default Count;
複製代碼
新增 test/Count.spec.tsx
組件
/** * @jest-environment jsdom */
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Count from '../Count';
describe('Count', () => {
it('should render without error', () => {
const { getByTestId } = render(<Count />);
const $count = getByTestId('count');
const $button = getByTestId('button');
expect($count).toHaveTextContent('0');
fireEvent.click($button);
expect($count).toHaveTextContent('1');
});
});
複製代碼
這裏經過 testId
來查找元素, 使用 fireEvent 觸發 click
事件
新增 Button.tsx
組件
import React from 'react';
type Props = {
onClick: () => void;
};
function Button({ onClick }: Props) {
return <button onClick={onClick}>button</button>;
}
export default Button;
複製代碼
添加 test/Button.spec.tsx
測試用例
/** * @jest-environment jsdom */
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import Button from '../Button';
describe('Button', () => {
it('should render without error', () => {
const handleClick = jest.fn(); // mock 函數
const { getByText } = render(<Button onClick={handleClick} />); // 傳遞 props
const $button = getByText('button');
fireEvent.click($button);
expect(handleClick).toHaveBeenCalled(); // 指望其被調用
});
});
複製代碼
// timer.ts
let cache = 'cache';
export default function timer() {
setTimeout(() => {
cache = '';
}, 1000);
return cache;
}
複製代碼
// test/timer.spec.ts
import timer from '../timer'
jest.useFakeTimers(); // 替代原生計時器
describe('timer', () => {
it('should clear cache after timer out', () => {
expect(timer()).toBe('cache');
jest.advanceTimersByTime(1000); // 讓計時器前進 1000ms
expect(timer()).toBe('');
})
})
複製代碼
要測試的模塊可能依賴於其餘模塊或者第三方 npm 包的結果, 咱們能夠使用 Mock Functions 對其進行 mock
// test/mock.spec.ts
import { mocked } from 'ts-jest/utils';
import plus from '../plus';
jest.mock('../plus');
describe('mock', () => {
it('should return mock value', () => {
mocked(plus). (50);
expect(plus(1, 1)).toBe(50);
});
});
複製代碼
還有官網 mock axios npm 模塊的例子 jestjs.io/docs/en/moc…
有的模塊會從環境變量和命令行參數取值, 而且多是在模塊初始化時獲取的
// process.ts
const { env, argv } = process;
export function getEnvironmentValue() {
return env.Value;
}
export function getProcessArgsValues() {
return argv[2];
}
複製代碼
這種狀況咱們須要在每一個測試用例中, 使用動態 require 來運行時引入改模塊, 而且設置其每次引入時刪除 cache
// test/process.spec.ts
describe('mock process', () => {
beforeEach(() => {
jest.resetModules();
});
it('should return environment value', () => {
process.env = {
Value: 'value',
};
const { getEnvironmentValue } = require('../process');
expect(getEnvironmentValue()).toBe('value');
});
it('should return process args value', () => {
process.argv = ['value'];
const { getProcessArgsValues } = require('../process');
expect(getProcessArgsValues()).toBe('value');
});
});
複製代碼