轉載位址
React 16 Jest手動模拟(Manual Mocks)
項目初始化
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
手動模拟(Manual Mocks)
手動模拟主要功能是用于存儲模拟的資料。
例如,我可能希望建立一個允許您使用虛假資料的手動模拟,而不是通路網站或資料庫等遠端資源。
這可以確定您的測試快速且不易碎(not flaky)。
模拟水果子產品(Mocking fruit modules)
通過在緊鄰子產品的__mocks__/子目錄中編寫子產品來定義手動模拟。這個方式我在前面文章中的執行個體中也有用到過,具體的可以參考之前的文章,這裡我說下大概的流程
例如,要在src/lib目錄中模拟一個名為fruit的子產品,則分别建立檔案src/lib/fruit.js和檔案src/lib/__mocks__/fruit.js的檔案。
請注意__mocks__檔案夾區分大小寫。如果命名目錄是__MOCKS__,則可能在某些系統上測試的時候會中斷。
注意點
當我們在測試中需要該子產品時,還需要顯式的調用jest.mock('./moduleName')。
模拟Node核心子產品(Mocking Node modules)
如果正在模拟的子產品是Node module(例如:lodash),則模拟應放在與node_modules相鄰的__mocks__目錄中(除非您将根配置為指向項目根目錄以外的檔案夾)并将自動模拟。
沒有必要顯式調用jest.mock('module_name')。
可以通過在與範圍子產品的名稱比對的目錄結構中建立檔案來模拟範圍子產品。
例如,要模拟名為@scope/project-name的作用域子產品,請在__mocks__/@scope/project-name.js建立一個檔案,相應地建立@scope/目錄。
如果我們想模拟Node的核心子產品(例如:fs或path),那麼明确地調用。
例如:jest.mock('path')是必需的,因為預設情況下不會模拟核心Node子產品。
執行個體示範
當給定子產品存在手動模拟時,Jest的子產品系統将在顯式調用jest.mock('moduleName')時使用該子產品。
但是,當automock設定為true時,即使未調用jest.mock('moduleName'),也将使用手動模拟實作而不是自動建立的模拟。
要選擇不使用此行為,您需要在應使用實際子產品實作的測試中顯式調用jest.unmock('moduleName')。
為了正确模拟,Jest需要jest.mock('moduleName')與require/import語句在同一範圍内。
假設我們有一個子產品,它提供給定目錄中所有檔案的摘要。在這種情況下,我們使用核心(内置)fs子產品來示範
src/lib/FileSummarizer.js
const fs = require('fs');
function summarizeFilesInDirectorySync(directory) {
return fs.readdirSync(directory).map(fileName => ({
directory,
fileName,
}));
}
exports.summarizeFilesInDirectorySync = summarizeFilesInDirectorySync;
由于我們希望我們的測試避免實際操作磁盤(這非常慢且易碎[fragile]),我們通過擴充自動模拟為fs子產品建立手動模拟。
我們的手動模拟将實作我們可以為我們的測試建構的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;
現在我們編寫測試。
請注意,我們需要明确告訴我們要模拟fs子產品,因為它是一個核心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生成自動模拟,并覆寫其預設行為。
這是推薦的方法,但完全是可選的。
如果您根本不想使用自動模拟,則隻需從模拟檔案中導出自己的函數即可。
完全手動模拟的一個缺點是它們是手動的 - 這意味着你必須在它們模拟的子產品發生變化時手動更新它們。
是以,最好在滿足您的需求時使用或擴充自動模拟。
為了確定手動模拟及其實際實作保持同步,在手動模拟中使用require.requireActual(moduleName)并在導出之前使用模拟函數修改它可能是有用的。
項目實踐位址
https:// github.com/durban89/webpack4-react16-reactrouter-demo.git
tag:v_1.0.28