本文へスキップ

Henka ​

変化(henka)。 テストのためのテストです。Henka はコードをわざと小さく 1 か所ずつ壊し、そのたびにテストを実行して、どのテストも捕まえられなかった変更を一覧にします。カバレッジがわかるのは、その行が実行されたかどうかだけです。Henka は、その行の動きにテストが本当に頼っているかを確かめます。

GitHub · Keiko のアダプター

実行する ​

Vitest 5 以降でテストしているプロジェクトで実行します。

bash
npx henka                  # mutate src/
npx henka src/physics.ts   # one file or folder
npx henka --since main     # only the lines changed since main
✔ caught   src/score.ts:3:15 hits * 2 → hits / 2
✖ missed   src/score.ts:2:12 hits < 3 → hits <= 3
✖ missed   src/unused.ts:1:40 n * 2 → n / 2

missed: no test failed, so nothing tests what these change. Add a test of the behaviour they control.
  src/score.ts:2:12 hits < 3 → hits <= 3
  src/unused.ts:1:40 n * 2 → n / 2: no test loads this file

hits < 3 → hits <= 3 が missed になったのは、2 つの式の結果が分かれる唯一の値 combo(3) を呼ぶテストがないからです。そのテストを書けば解決します。missed がひとつでもあると終了コードは 1 になるので、CI のゲートに使えます。結果は mutants.out/(outcomes.json と結果ごとのテキストファイル)に書き出され、エージェントや CI から読めます。

何を変えるか ​

変更例
算術+ ↔ -、* ↔ /、% → *、+= ↔ -=、*= ↔ /=、++ ↔ --
比較< ↔ <=、> ↔ >=、=== ↔ !==、== ↔ !=
論理&& ↔ ||、!x → x
符号-x → x
数値n → 0、0 → 1
関数の本体空にするか、戻り値の型に合わせて 0、""、true、false を返す

文字列の連結、型、import、オブジェクトのキーには手を付けません。行の上か同じ行に // henka-skip と書くとその行を飛ばし、ファイルのどこかに // henka-skip-file と書くとファイルごと飛ばします。

結果の種類 ​

結果意味
caughtテストが失敗しました。この変更が影響する部分はテストされています。
missedすべてのテストが通りました。この変更が影響する部分を確かめるテストがありません。その振る舞いのテストを書きましょう。
held無視してよい変更で、すべてのテストが通りました。Keiko のシードの変更で使います。
brittle無視してよいはずの変更(乱数のシードなど)でテストが失敗しました。
unviable変更を適用できませんでした。

実行のしくみ ​

Henka はまず何も変えずに、対象のファイルに関係するテストを実行し、どのテストファイルがどのソースファイルを読み込むかを記録します。そのあと、ジョブごとに Vitest のプロセスを 1 つ開いたままにして、変更を 1 つずつ順に試します。変更したコードはファイルを読み込むときに Vite プラグインが差し替えるので、ディスク上のファイルは変わりません。実行するのはそのファイルを読み込むテストファイルだけで、最初の失敗で止まります。ベースラインの 5 倍(最低 20 秒)を超えた実行は止めて caught に数えます。変更でテストが止まらなくなったということだからです。

オプション
--since <ref>git の ref 以降に変わった行だけを対象にします。新しいファイルは全体が対象です。
--jobs <n>同時に試す変更の数(デフォルトはコア数の半分、最大 4)。
--timeout <s>実行をこの秒数で止めます。
--root <dir>Vitest が設定を探す場所。
--out <dir>結果の書き出し先(デフォルトは mutants.out)。
--list変更を実行せずに一覧表示します。
--jsonレポートを JSON で出力します。

Web ゲーム ​

npx henka には Vitest が必要です。debuff のゲームのテストは debuff test で実行され、これは Keiko の上で動きます。そのため debuff では Keiko のアダプター npx keiko mutate を使いましょう。こちらはコードではなくゲームを変えます。パラメータを範囲の両端にし、乱数のシードを変え、フレームレートを下げます。使い方をテストをテストするで説明しています。

独自のアダプター ​

コアはそれ自体では何も実行しません。テストの実行と変更の適用はアダプターが担うので、どのテストランナーでも使えます。

ts
import { type Adapter, formatReport, mutate, writeOut } from "henka";

const adapter: Adapter = {
  name: "my-runner",
  baseline: () => runMyTests(),
  mutants: () => [{ id: "timeout=0", label: "request timeout 30 s → 0", expect: "noticed" }],
  run: (mutant) => runMyTests({ apply: mutant.id }),
};

const report = await mutate(adapter, { jobs: 4 });
writeOut(report, "mutants.out");
console.log(formatReport(report));

henka/source を使うと、任意の ESTree パーサーでソースの変更を見つけて適用できます。henka/vite の Vite プラグインは、Vite を通してコードを読み込むアダプターで使えます。