備忘録です。たまに忘れるので記録用に残しておきます。
mock-fs
最強のファイルシステム用モックライブラリです。これを使うことで、fs
モジュールを使ったコードやライブラリを、実際のファイルシステムを使うこと無くテストすることができます。
const assert = require('node:assert/strict');
const fs = require('node:fs/promises');
const path = require('node:path');
const { test } = require('node:test');
const mockFs = require('mock-fs');
test('ファイルの読み込みをモックするテスト', async () => {
try {
mockFs({
'anser.txt': '42',
});
const filepath = path.join(process.cwd(), 'anser.txt');
const filedata = await fs.readFile(filepath, { encoding: 'utf8' });
assert.strictEqual(filedata, '42');
} finally {
mockFs.restore();
}
});
mock-fsが最強である理由は、これがECMAScript Modulesでも使える事です。嘘ではありません。Jestのjest.mock('fs');
みたいなテストコードの動的書き換えすら要らず、Node.jsネイティブのESMでも使えます。試しに以下のコードを拡張子.mjs
のファイルに保存してnodeコマンドで実行してみてください。
import * as assert from 'node:assert/strict';
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { test } from 'node:test';
import mockFs from 'mock-fs';
test('ファイルの読み込みをモックするテスト', async () => {
try {
mockFs({
'anser.txt': '42',
});
const filepath = path.join(process.cwd(), 'anser.txt');
const filedata = await fs.readFile(filepath, { encoding: 'utf8' });
assert.strictEqual(filedata, '42');
} finally {
mockFs.restore();
}
});
これが可能なのは、mock-fsがfs
モジュールではなく、fs
モジュールが内部で使用しているinternalBinding('fs')
を書き換えているからです。require('fs')
はモックしていないので、ESMだろうと正しくモックできるというわけです。
これを知った時は心底驚いた。
nock
HTTP通信用のモックライブラリです。だいぶ前に見つけて以来、愛用しています。
spawk
サブプロセス用のモックライブラリです。child_process
モジュールを使っているコードのテストに使えます。
vitest-mock-process / jest-mock-process
process.stdout
、process.stderr
、console.log()
用のモックライブラリです。名前の通り、VitestまたはJest向け。stdoutやstderrに何かを書き込む関数のテストに最適。
あとがき
- ファイルシステム
- HTTP通信
- サブプロセス
- stdoutとstderr
この4種類をモックできれば、大抵のテストコードで困ることは無いでしょう。個人的には困っていません。
モックというと、たいていのテストコードでは関数そのものやライブラリそのものをモックする方法がよく取られているように思います。しかし、依存しているライブラリの内部挙動が変更されたことでバグが発生することもあります。依存するライブラリの正しさを含めてこそのテスト。そう考えるならば、やはりNode.jsが提供するI/Oそのものをモックする方法が最適だと考えます。