ファイルの読み書きを伴うモジュールのDeno.test


下記の様なts-morphを使用したoverloadを追加する関数のテストを用意してみます。

import { FunctionDeclaration } from "https://deno.land/x/[email protected]/mod.ts";

type Parameter = { name: string; type: string };
type Entry = { parameters: Parameter[] };

export const addCustomEntries = (
  entries: Entry[],
  constructor: FunctionDeclaration
) => {
  constructor.addOverloads(
    entries.map((entry) => ({
      parameters: entry.parameters,
      returnType: "string",
    }))
  );
};

helperの定義

ファイルの読み書きを行うために前後処理を実行するヘルパー関数を定義します。

helper.ts
import {
  Project,
  SourceFile,
  ManipulationSettings,
  IndentationText,
} from "https://deno.land/x/[email protected]/mod.ts";

export const withDir = async (
  cb: (tempDir: string) => void | Promise<void>
) => {
  const tempDir = await Deno.makeTempDir({
    prefix: "deno_couscous_test_",
  });
  await cb(tempDir);
  await Deno.remove(tempDir, { recursive: true });
};

export const withSource = (
  cb: (tempSrc: SourceFile) => void | Promise<void>,
  manipulationSettings: Partial<ManipulationSettings> = {
    indentationText: IndentationText.TwoSpaces,
  }
) =>
  withDir(async (tempDir) => {
    const tempFile = await Deno.makeTempFile({ dir: tempDir });
    const project = new Project({
      libFolderPath: tempDir,
      manipulationSettings,
    });
    const source = project.addSourceFileAtPath(tempFile);
    await cb(source);
  });

加えてfile同士を比較するアサーションの定義もしておきます。

assertEqualFiles.ts
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";

export const assertEqualFile = async (
  receivedPath: string,
  expectedPath: string
) => {
  const [received, expected] = await Promise.all([
    Deno.readTextFile(receivedPath),
    Deno.readTextFile(expectedPath),
  ]);
  await assertEquals(received, expected);
};

テストケースの実装

今回はスナップショットテストの出力結果をハードコーディングしていますが、実装の仕方によっては自動生成も可能だと思います。

output.ts.snapshot
function hoge(piyo: string): string;
function hoge(piyo: string, foo: number): string;
function hoge(foo: number, bar: boolean): string;
function hoge(piyo: string, foo: number, bar: number): string;
function hoge() {
  return ""
}

あくまでoverloadの検査であって、関数自体の検査は含まれていないので最低限の実装をテスト前に追加します。

test.ts
import { StructureKind } from "https://deno.land/x/[email protected]/mod.ts";
import { withSource } from "./helper.ts";
import { assertEqualFile } from "./assertEqualFiles.ts";
import { addCustomEntries } from "./mod.ts";

Deno.test(
  "addCustomEntries",
  { permissions: { read: true, write: true } },
  () =>
    withSource(async (source) => {
      const constructor = source.addFunction({
        name: "hoge",
        kind: StructureKind.Function,
        statements: 'return ""',
      });
      addCustomEntries(
        [
          {
            parameters: [{ name: "piyo", type: "string" }],
          },
          {
            parameters: [
              { name: "piyo", type: "string" },
              { name: "foo", type: "number" },
            ],
          },
          {
            parameters: [
              { name: "foo", type: "number" },
              { name: "bar", type: "number" },
            ],
          },
          {
            parameters: [
              { name: "piyo", type: "string" },
              { name: "foo", type: "number" },
              { name: "bar", type: "number" },
            ],
          },
        ],
        constructor
      );
      await source.save();
      assertEqualFile(source.getFilePath(), "output.ts.snapshot");
    })
);

実行結果

追記の余談

deno test実行時にフラグを渡すことができるのでアップデート用のフラグを追加することでスナップショットの結果を自動で更新できる様にしてみます。

import {
  assertEquals,
  equal,
} from "https://deno.land/[email protected]/testing/asserts.ts";
import { copy } from "https://deno.land/[email protected]/fs/mod.ts";

export const assertSnapshot = async (
  receivedPath: string,
  expectedPath: string
) => {
  const [received, expected] = await Promise.all([
    Deno.readTextFile(receivedPath),
    Deno.readTextFile(expectedPath),
  ]);
  if (Deno.args.includes("update_snapshot")) {
    if (equal(received, expected)) return;
    await copy(receivedPath, expectedPath, { overwrite: true });
  } else {
    assertEquals(received, expected);
  }
};

これで以下の様にスナップショットを更新するためのフラグを追加することができました🎉
deno test test.ts --allow-read --allow-write -- update_snapshot