React 16 Jest手動(dòng)模擬(Manual Mocks)
項(xiàng)目初始化
git clone https://github.com/durban89/webpack4-react16-reactrouter-demo.git
cd webpack4-react16-reactrouter-demo
git fetch origin
git checkout v_1.0.27
npm install
手動(dòng)模擬(Manual Mocks)
手動(dòng)模擬主要功能是用于存儲(chǔ)模擬的數(shù)據(jù)。
例如,我可能希望創(chuàng)建一個(gè)允許您使用虛假數(shù)據(jù)的手動(dòng)模擬,而不是訪問網(wǎng)站或數(shù)據(jù)庫(kù)等遠(yuǎn)程資源。
這可以確保您的測(cè)試快速且不易碎(not flaky)。
模擬水果模塊(Mocking fruit modules)
通過在緊鄰模塊的mocks/子目錄中編寫模塊來定義手動(dòng)模擬。這個(gè)方式我在前面文章中的實(shí)例中也有用到過,具體的可以參考之前的文章,這里我說下大概的流程
例如,要在src/lib目錄中模擬一個(gè)名為fruit的模塊,則分別創(chuàng)建文件src/lib/fruit.js和文件src/lib/mocks/fruit.js的文件。
請(qǐng)注意mocks文件夾區(qū)分大小寫。如果命名目錄是MOCKS,則可能在某些系統(tǒng)上測(cè)試的時(shí)候會(huì)中斷。
注意點(diǎn)
當(dāng)我們?cè)跍y(cè)試中需要該模塊時(shí),還需要顯式的調(diào)用jest.mock('./moduleName')。
模擬Node核心模塊(Mocking Node modules)
如果正在模擬的模塊是Node module(例如:lodash),則模擬應(yīng)放在與node_modules相鄰的mocks目錄中(除非您將根配置為指向項(xiàng)目根目錄以外的文件夾)并將自動(dòng)模擬。
沒有必要顯式調(diào)用jest.mock('module_name')。
可以通過在與范圍模塊的名稱匹配的目錄結(jié)構(gòu)中創(chuàng)建文件來模擬范圍模塊。
例如,要模擬名為@scope/project-name的作用域模塊,請(qǐng)?jiān)?strong>mocks/@scope/project-name.js創(chuàng)建一個(gè)文件,相應(yīng)地創(chuàng)建@scope/目錄。
注意點(diǎn)
如果我們想模擬Node的核心模塊(例如:fs或path),那么明確地調(diào)用。
例如:jest.mock('path')是必需的,因?yàn)槟J(rèn)情況下不會(huì)模擬核心Node模塊。
實(shí)例演示
當(dāng)給定模塊存在手動(dòng)模擬時(shí),Jest的模塊系統(tǒng)將在顯式調(diào)用jest.mock('moduleName')時(shí)使用該模塊。
但是,當(dāng)automock設(shè)置為true時(shí),即使未調(diào)用jest.mock('moduleName'),也將使用手動(dòng)模擬實(shí)現(xiàn)而不是自動(dòng)創(chuàng)建的模擬。
要選擇不使用此行為,您需要在應(yīng)使用實(shí)際模塊實(shí)現(xiàn)的測(cè)試中顯式調(diào)用jest.unmock('moduleName')。
注意點(diǎn)
為了正確模擬,Jest需要jest.mock('moduleName')與require/import語(yǔ)句在同一范圍內(nèi)。
假設(shè)我們有一個(gè)模塊,它提供給定目錄中所有文件的摘要。在這種情況下,我們使用核心(內(nèi)置)fs模塊來演示
src/lib/FileSummarizer.js
const fs = require('fs');
function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}
exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;
由于我們希望我們的測(cè)試避免實(shí)際操作磁盤(這非常慢且易碎[fragile]),我們通過擴(kuò)展自動(dòng)模擬為fs模塊創(chuàng)建手動(dòng)模擬。
我們的手動(dòng)模擬將實(shí)現(xiàn)我們可以為我們的測(cè)試構(gòu)建的fs API的自定義版本:
src/lib/mocks/fs.js
const path = require('path');
const fs = jest.genMockFromModule('fs');
let mockFiles = Object.create(null);
function __setMockFiles(newMockFiles) {
mockFiles = Object.create(null);
const keys = Object.keys(newMockFiles);
for (let index = 0; index < keys.length; index += 1) {
const file = keys[index];
const dir = path.dirname(file);
if (!mockFiles[dir]) {
mockFiles[dir] = [];
}
mockFiles[dir].push(path.basename(file));
}
}
function readdirSync(directoryPath) {
return mockFiles[directoryPath] || [];
}
fs.__setMockFiles = __setMockFiles;
fs.readdirSync = readdirSync;
module.exports = fs;
現(xiàn)在我們編寫測(cè)試。
請(qǐng)注意,我們需要明確告訴我們要模擬fs模塊,因?yàn)樗且粋€(gè)核心Node模塊:
src/tests/FileSummarizer-test.js
const fs = require('fs');
const FileSummarizer = require('../lib/FileSummarizer');
jest.mock('fs');
describe('listFilesInDirectorySync', () => {
const MOCK_FILE_INFO = {
'/path/to/file1.js': 'console.log("file1 contents");',
'/path/to/file2.txt': 'file2 contents',
};
beforeEach(() => {
// Set up some mocked out file info before each test
fs.__setMockFiles(MOCK_FILE_INFO);
});
test('includes all files in the directory in the summary', () => {
const fileSummary = FileSummarizer.summarizeFilesInDirectorySync('/path/to');
expect(fileSummary.length).toBe(2);
});
});
此處顯示的示例模擬使用jest.genMockFromModule生成自動(dòng)模擬,并覆蓋其默認(rèn)行為。
這是推薦的方法,但完全是可選的。
如果您根本不想使用自動(dòng)模擬,則只需從模擬文件中導(dǎo)出自己的函數(shù)即可。
完全手動(dòng)模擬的一個(gè)缺點(diǎn)是它們是手動(dòng)的 - 這意味著你必須在它們模擬的模塊發(fā)生變化時(shí)手動(dòng)更新它們。
因此,最好在滿足您的需求時(shí)使用或擴(kuò)展自動(dòng)模擬。
為了確保手動(dòng)模擬及其實(shí)際實(shí)現(xiàn)保持同步,在手動(dòng)模擬中使用require.requireActual(moduleName)并在導(dǎo)出之前使用模擬函數(shù)修改它可能是有用的。
項(xiàng)目實(shí)踐地址
https://github.com/durban89/webpack4-react16-reactrouter-demo.git
tag:v_1.0.28