本文へスキップ

Keiko ​

稽古(Keiko):practice。 Web ゲームのための、速くて決定的なテストです。

Keiko はあなたの本物のゲームを Node 上で動かします。ブラウザは使わず、何も描画しません。時間・入力・乱数はテストが指示したときだけ進むので、何度実行しても同じ展開になり、一般的なテストは数十ミリ秒で終わります。

ts
import { expect, play, test } from "keiko";

test("the player reaches top speed within a quarter second", async () => {
  const game = await play();
  game.press("ArrowRight");
  expect(game.until((s) => s.player.vx >= 140)).toBeAtMost(15);
});

はじめる · エンジン別レシピ · API リファレンス

なぜ Keiko なのか ​

ゲームのバグの多くは時間の経過に伴う振る舞いにまつわるものです。ジャンプが前より少し低い、敵の振り向きが 1 フレーム遅い、高速のダッシュが壁をすり抜ける、2 回プレイすると展開が違う。ブラウザ自動化はリアルタイムで動き、ゲームをステップ実行できないので、こうした疑問にうまく答えられません。ユニットテストも同じです。テストするのは関数であって、ゲームそのものではないからです。

Keiko はゲームそのものを 1 フレームずつプレイし、ゲームらしい問いを投げかけられるようにします。

  • あとどれくらいで? game.until((s) => s.player.onGround) はかかったステップ数を返します。
  • 一度も起きない? game.during("3s", (s) => s.player.x >= 0) は不変条件が破れたまさにそのステップで失敗します。
  • どれくらい高く、どれくらい速く? game.trace("player.y", "1s").min() で動きを測れます。
  • 出荷するかもしれないすべての値で? test.sweep(…, { "player.jumpSpeed": [260, 290, 320] }, …)。
  • 毎回同じ? test.deterministic(…) は異なるシードで 2 回プレイし、全ステップを比較します。

失敗したときは、その瞬間のゲームを見せてくれます。tick、シーン、ステート、テストの該当行、そしてコンソールです。

どんなエンジンでも ​

Keiko はエンジンの API ではなく Web プラットフォームを通して動きます。ゲームはヘッドレスなページで動き、requestAnimationFrame、タイマー、時計は仮想化され、入力は本物の DOM イベントとして届き、Canvas 2D と WebGL はあらゆる呼び出しを受け付けます。動作確認済みのエンジンは次のとおりです。

エンジンキーボードポインター決定的
debuff✔✔✔
Phaser 4✔✔✔
PixiJS 8✔✔✔
KAPLAY✔✔✔
Excalibur✔✔✔
three.js✔✔✔
手書きのループ✔✔✔

ゲームのステートを読むには、開発ビルドでスナップショットを公開します。ほとんどのゲームで必要な変更はこれだけです。

ts
if (import.meta.env.DEV) window.__KEIKO__ = { state: () => ({ player: { x: player.x, y: player.y }, score }) };

エージェントのためにも ​

Keiko の結果は構造化されています。npx keiko --json はすべての失敗を、メッセージ、ソースの行、ゲームのステートとともに報告します。コーディングエージェントは、テストを書いて失敗を確かめ、ゲームを直して直ったことを証明するまでを、ブラウザなしでこなせます。debuff プロジェクトでは、MCP サーバーの run_tests ツールから直接実行できます。

守備範囲 ​

Keiko が扱うのはゲームが何をするかです。ピクセルは見ませんし、音も聞きません。そちらは debuff のシナリオのような、本物のブラウザでのスクリーンショットテストを使ってください。ほとんどのプロジェクトでは両方あるのが理想です。高速なロジックテストをたくさん、見た目のチェックを少し。

Keiko は MIT ライセンスで、debuff リポジトリで開発されています。