【実務・中級編】ESLintの『カスタムルールのテスト』を極める:テストコードを書いてLintルール自体の品質を保証する – デバッグ・コード品質・テストツール生産性向上バイブル

ESLintの「カスタムルール」をテストせよ:Lint運用をブラックボックス化させない最強の守護術

チーム開発において、ESLintのカスタムルールを作成することは「特定の設計思想を強制する強力な武器」になります。しかし、そのルール自体が不完全であれば、CIで誤検知を連発し、開発者の信頼を失い、かえって生産性を阻害します。

本稿では、`RuleTester`を用いたカスタムルールの品質保証術を軸に、ESLintの真の実力を引き出すアーキテクチャ設計について伝授します。

—

1. なぜ「RuleTester」を避けて通れないのか

カスタムルールはAST(抽象構文木)を操作する性質上、予期せぬエッジケースが頻発します。手動でプロジェクトに組み込んでLintを走らせるようなデバッグは、開発スピードを著しく低下させます。

`RuleTester`はESLintが公式に提供するテストユーティリティであり、「特定のコードが期待通りに検証されるか」を分離して検証するための最高峰のツールです。

RuleTesterの基本形と設計思想

単に「エラーが出るか」を確認するだけでは不十分です。以下の3つの観点を必ず網羅してください。

  • Valid (正常系): ルールが適用されるべきでないコードが、正しくスルーされるか。
  • Invalid (異常系): ルールが適用されるべきコードが、意図したエラーメッセージ・修正案を出すか。
  • Fixer (自動修正): `fixable`なルールの場合、修正後のコードが構文として正しいか。

// rule-testerの導入例
const { RuleTester } = require(“eslint”);
const rule = require(“./my-custom-rule”);

const ruleTester = new RuleTester({
// パーサー設定をテスト環境と同期させる(TSの場合は必須)
parserOptions: { ecmaVersion: 2020, sourceType: “module” }
});

ruleTester.run(“my-custom-rule”, rule, {
valid: [“const validVariable = 1;”], // 正常系:静的解析でエラーが出ないはずのコード
invalid: [
{
code: “const var = 1;”, // 異常系:エラーを誘発するコード
errors: [{ messageId: “unexpectedVar” }], // 検証すべきエラーID
output: “const validVar = 1;” // 自動修正後の期待値(Fixerテスト)
}
]
});

—

2. 開発効率を爆速化する「隠れた」設定とテクニック

VS Codeの神設定:Lintのフィードバックループを最短にする

`eslint.rules.customizations` を `settings.json` に追記することで、ルール開発中のストレスが激減します。

// .vscode/settings.json
{
“eslint.validate”: [“javascript”, “typescript”],
// エラーの重要度を強制的にオーバーライド
“eslint.rules.customizations”: [
{ “rule”: “my-plugin/my-custom-rule”, “severity”: “warn” }
],
// ファイル保存時にPrettierとESLintを競合させず実行する
“editor.codeActionsOnSave”: {
“source.fixAll.eslint”: “always”
}
}

チーム開発の黄金律:「設定の継承」と「シェアード設定」

個別のリポジトリに巨大な `eslintrc` を置くのはアンチパターンです。「Core Plugin」と「Project Config」を分離し、npmパッケージ経由で配布してください。

  • `eslint-config-company-base`: 共通のコーディング規約。
  • `eslint-plugin-company-internal`: ビジネスロジックに関連するカスタムルール。

これにより、ルールを修正した際に全プロダクトへ一括適用が可能になります。

—

3. 実践:複雑なルールの品質を保証する「テストケース設計術」

正規表現やASTの深掘りロジックを含むルールでは、以下の「境界値テスト」を実装してください。

1. ネストの深さ: `IfStatement`の中に`IfStatement`がある場合の挙動。
2. スコープの隔離: 同じ変数名でもブロックが異なれば別物として扱えるか(`scope.through`や`scope.variables`の確認)。
3. コメントの無視: コード内に含まれるコメントが解析に悪影響を及ぼさないか。

実践的なテストコードの構築例

// テストケースのデータ構造化
const testCases = [
{
name: “ネストされた関数内でも正しく警告を出す”,
code: “function outer() { function inner() { var x = 1; } }”,
errors: [{ messageId: “avoidVar” }]
},
{
name: “コメントアウトされたコードは無視する”,
code: “// var x = 1;”,
errors: []
}
];

// ループでテストを実行し、可読性と保守性を担保する
testCases.forEach(({ name, code, errors }) => {
ruleTester.run(name, rule, {
valid: errors.length === 0 ? [code] : [],
invalid: errors.length > 0 ? [{ code, errors }] : []
});
});

—

4. アーキテクトからの提言:ルールは「ドキュメント」である

最後に、最も重要なことを伝えます。「ESLintのルールは、チームに対するドキュメントである」と認識してください。

ルールが違反した際に表示するメッセージには、「なぜそれがダメなのか」「どう修正すべきか」を明確に記述しましょう。テストコードの中で `errors` を検証する際、単にメッセージの存在確認をするのではなく、具体的なフィードバック内容までテスト対象に含めてください。

まとめ:明日からすべきこと
1. 既存のカスタムルールに `RuleTester` を適用する。
2. `Fixer` のテストを追加し、自動修正の安全性を担保する。
3. `eslint-plugin` を分離し、全プロジェクトで共有可能なパッケージを作る。

これらを徹底することで、あなたのチームのコードベースは「規律ある美しい状態」を維持し続け、レビューコストは劇的に削減されるはずです。技術とは、自動化することではなく、「人間が判断しなくていいことを機械に託すこと」なのです。

タイトルとURLをコピーしました