ユニットテストしたい(vitest)
$ npx vitest run
$ npx vitest
vitestはViteベースの高速なユニットテストフレームワークです。
TypeScriptにネイティブ対応しているため、ts-jestのような別プラグインなしでそのまま使えます。
vitest runは1回だけ実行して終了します。
引数なしのvitestはウォッチモードで起動し、ファイルの変更を検知して自動的に再実行します。
インストールしたい
$ npm install --save-dev vitest @vitest/coverage-v8
vitest本体と、カバレッジ計測用の@vitest/coverage-v8をdevDependenciesに追加します。
スクリプト設定したい(package.json)
{
"name": "...",
"scripts": {
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"...": "..."
}
}
$ npm test
$ npm run test:watch
$ npm run test:coverage
testは1回実行、test:watchはウォッチモード、test:coverageはカバレッジ計測付きで実行します。
用途に応じて使い分けます。
設定したい(vitest.config.ts)
1import { defineConfig } from "vitest/config";
2
3export default defineConfig({
4 test: {
5 globals: true,
6 environment: "node",
7 include: ["**/__tests__/**/*.test.ts"],
8 setupFiles: ["__tests__/setup.ts"],
9 coverage: {
10 provider: "v8",
11 include: ["src/**/*.ts"],
12 },
13 },
14});
globals: trueにすると、describe・test・expect・viなどを
importせずにそのままグローバルに使えるようになります。
jestから移行する場合、書き方を大きく変えずに済むので便利です。
environment: "node"は、テストを実行する環境の指定です。
GASにはブラウザDOMがないため、nodeを指定します。
includeでテスト対象のファイルパターンを、
setupFilesでテスト実行前に共通で読み込むファイルを指定します。
coverageでカバレッジ計測の設定ができます。
provider: "v8"は、Node.js(V8エンジン)に組み込まれたカバレッジ計測機能を使う設定です。
GASのグローバルをモックしたい(__tests__/setup.ts)
1// GASのグローバルオブジェクトをモックする
2/// <reference types="google-apps-script" />
3
4(global as any).Logger = {
5 log: vi.fn(),
6 clear: vi.fn(),
7};
8
9(global as any).console = {
10 log: vi.fn(),
11 info: vi.fn(),
12 warn: vi.fn(),
13 error: vi.fn(),
14};
DriveAppやSpreadsheetApp、LoggerのようなGASのグローバル変数は、
Node.js環境(vitestの実行環境)には存在しません。
テストを実行する前に、setupFilesで指定したファイルの中で、
必要な分だけvi.fn()でモックしておく必要があります。
ヒント
すべてのGASサービスを一度にモックする必要はありません。 テスト対象のコードが実際に呼び出している範囲だけモックすれば十分です。
ユニットテストしたい
1import { Counter } from "../src/counter";
2
3describe("Counter", () => {
4 describe("constructor", () => {
5 test("should create counter with default settings", () => {
6 const counter = new Counter();
7 expect(counter.getValue()).toBe(0);
8 });
9 });
10
11 describe("increment", () => {
12 let counter: Counter;
13
14 beforeEach(() => {
15 counter = new Counter();
16 });
17
18 test("should increment the value", () => {
19 expect(counter.increment()).toBe(1);
20 });
21 });
22});
describeでテスト対象をグループ化し、test(itのエイリアス)で個々のテストケースを書きます。
describeは入れ子にできるので、以下のような構造を意識して作成するとよいです。
テストするクラス名
テストするメソッド1
正常系テストたち
異常系テストたち
テストするメソッド2
正常系テストたち
異常系テストたち
beforeEachで、各テストの実行前に共通のセットアップ処理を行えます。
ヒント
テストケースを自分で考えるのは大変です。 最近はClaudeに、アップロードしたソースコードを読み込んでもらい、 それに対するユニットテストを作ってもらうようにしています。