経験は何よりも饒舌

10年後に真価を発揮するかもしれないブログ 

スナップショットテストのテストにスナップショットを使う

github.com

testing/snapshot.tsgreen(bold(`\n > ${updated} snapshots updated.`))の出力に影響する部分のリファクタリングをする際、異なるディレクトリにあるスナップショットテストの実行結果をテストで保証したかった。

既存のコードを読んだら、スナップショットテストのテストにスナップショットを使っていたのでおもしろかったので、追加したテストについてメモしておく。


まず、一時的なディレクトリを作成し、受け取った関数を実行し、実行後に一時的なディレクトリを削除する関数を用意する。

https://github.com/denoland/deno_std/blob/b7e6dc0914aeaa93b6af3f9db666eaaf7615f456/testing/snapshot_test.ts#L45-L62

function testFnWithDifferentTempDir(
  fn: (
    t: Deno.TestContext,
    tempDir1: string,
    tempDir2: string,
  ) => Promise<void>,
) {
  return async (t: Deno.TestContext) => {
    const tempDir1 = await Deno.makeTempDir();
    const tempDir2 = await Deno.makeTempDir();
    try {
      await fn(t, tempDir1, tempDir2);
    } finally {
      await Deno.remove(tempDir1, { recursive: true });
      await Deno.remove(tempDir2, { recursive: true });
    }
  };
}


実行される関数についてみていく。

引数に、スナップショットテストを記述した文字列を渡して関数を実行している。

https://github.com/denoland/deno_std/blob/b7e6dc0914aeaa93b6af3f9db666eaaf7615f456/testing/snapshot_test.ts#L629-L660

testFnWithDifferentTempDir(async (t, tempDir1, tempDir2) => {
...
    const result1 = await runTestWithUpdateFlag(
      `
      import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";

      Deno.test("Snapshot Test - First", async (t) => {
        await assertSnapshot(t, [
          1,
          2,
        ]);
      });
      Deno.test("Snapshot Test - Second", async (t) => {
        await assertSnapshot(t, [
          3,
          4,
        ]);
      });`,
      `
      import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";

      Deno.test("Snapshot Test - First", async (t) => {
        await assertSnapshot(t, [
          1,
          2,
        ]);
      });
      Deno.test("Snapshot Test - Second", async (t) => {
        await assertSnapshot(t, [
          3,
          4,
        ]);
      });`,
    );
...


実行される関数は、スナップショットテストを記述した文字列を一時的なディレクトリにあるファイルに書き込んで、スナップショットテストをコマンドで実行している。
その結果が返り値となっている。

https://github.com/denoland/deno_std/blob/b7e6dc0914aeaa93b6af3f9db666eaaf7615f456/testing/snapshot_test.ts#L594-L618

    const tempTestFileName = "test.ts";
    const tempTestFilePath1 = join(tempDir1, tempTestFileName);
    const tempTestFilePath2 = join(tempDir2, tempTestFileName);

    async function runTestWithUpdateFlag(test1: string, test2: string) {
      await Deno.writeTextFile(tempTestFilePath1, test1);
      await Deno.writeTextFile(tempTestFilePath2, test2);

      const command = new Deno.Command(Deno.execPath(), {
        args: [
          "test",
          "--allow-all",
          tempTestFilePath1,
          tempTestFilePath2,
          "--",
          "-u",
        ],
      });
      const { stdout, stderr } = await command.output();

      return {
        output: new TextDecoder().decode(stdout),
        error: new TextDecoder().decode(stderr),
      };
    }


その結果を、スナップショットテストする。

https://github.com/denoland/deno_std/blob/b7e6dc0914aeaa93b6af3f9db666eaaf7615f456/testing/snapshot_test.ts#L663-L665

    await assertSnapshot(t, formatTestOutput(result1.output), {
      name: "Snapshot Test - Different Dir - New snapshot",
    });

スナップショットが生成される。
これをみて、green(bold(`\n > ${updated} snapshots updated.`))の出力に影響がないか確認する。

https://github.com/denoland/deno_std/blob/b7e6dc0914aeaa93b6af3f9db666eaaf7615f456/testing/__snapshots__/snapshot_test.ts.snap#L517-L550

snapshot[`Snapshot Test - Different Dir - New snapshot 1`] = `
"running 2 tests from <tempDir>/test.ts
" +
  "Snapshot Test - First ... ok (--ms)
" +
  "Snapshot Test - Second ... ok (--ms)
" +
  "------- output -------
" +
  "
" +
  " > 2 snapshots updated.
" +
  "running 2 tests from <tempDir>/test.ts
" +
  "Snapshot Test - First ...----- output end -----
" +
  "Snapshot Test - First ... ok (--ms)
" +
  "Snapshot Test - Second ... ok (--ms)
" +
  "------- output -------
" +
  "
" +
  " > 2 snapshots updated.
" +
  "
" +
  "ok | 4 passed | 0 failed (--ms)
" +
  "
"
`;

それぞれの実行結果が2 snapshots updatedになっているのでテスト成功。