Vitest は JavaScript/TypeScript のテストフレームワークである。
Vite をインストールしていないプロジェクトでも使える。
インストールする
$ npm i -D vitest
package.json
に以下をマージする。
package.json
{
"scripts": {
+ "test": "vitest"
}
}
以下で*.test.ts
のテストコードを実行することができる。
$ npm run test
以下で特定のファイルのテストコードを実行することができる。
$ npx vitest src/path/to/code.test.ts
基本
import {
afterAll,
beforeAll,
beforeEach,
describe,
expect,
test,
} from "vitest";
describe("基本", () => {
beforeAll(() => {
console.log("すべてのテストが始まる前に呼ばれるコード");
});
beforeEach(() => {
console.log("各テストの前に毎回呼ばれるコード");
});
describe("値の確認系", () => {
test("toBe()はプリミティブな値を比較できる", () => {
expect(1 + 1).toBe(2);
});
test("toBe()はオブジェクトの参照が同じか確認できる", () => {
const obj = { key: "value" };
expect(obj).toBe(obj);
});
test("notを使うと否定できる", () => {
const obj1 = { key: "value" };
const obj2 = { key: "value" };
expect(obj1).not.toBe(obj2);
});
test("toEqual()はオブジェクトの値が等しいことを確認できる", () => {
expect(["a", "b"]).toEqual(["a", "b"]);
});
test("toHaveProperty()はプロパティを持っていることを確認できる", () => {
expect({ key: "value" }).toHaveProperty("key");
});
test("toBeDefined()はnullishでないことを確認できる", () => {
expect({}).toBeDefined();
});
});
afterAll(() => {
console.log("すべてのテストが終わったときに呼ばれるコード");
});
});
例外の捕捉系
import { describe, expect, test } from "vitest";
describe("例外の捕捉系", () => {
test("同期的に特定の例外が発生することを確認できる", async () => {
function throwingErrorFunction() {
throw new Error("Something happened");
}
expect(throwingErrorFunction).toThrow(new Error("Something happened"));
});
test("引数を渡しつつ同期的に特定の例外が発生することを確認できる", async () => {
function throwingErrorFunction(args: string) {
throw new Error("Something happened");
}
expect(() => throwingErrorFunction("args")).toThrow(
new Error("Something happened")
);
});
test("非同期的に特定の例外が発生することを確認できる", async () => {
async function throwingErrorAsyncFunction(): Promise<void> {
return new Promise((resolve) => {
throw new Error("Something happened");
});
}
expect(throwingErrorAsyncFunction).rejects.toThrow(
new Error("Something happened")
);
});
});
時間がかかる系
import { describe, expect, test } from "vitest";
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
describe("時間がかかる系", () => {
test(
"テストに5秒以上かかるとタイムアウトでNGになるが{ timeout: Infinity }を指定してやると5秒以上のテストも実施できる",
{ timeout: Infinity },
async () => {
const then = performance.now();
await sleep(10000);
const now = performance.now();
expect(now - then).greaterThanOrEqual(9900);
}
);
});
モック系
import { describe, expect, test, vitest } from "vitest";
class NotToBeTestedClass {
sayHello() {
return "Hello";
}
}
class ToBeTestedClass {
constructor(public readonly notToBeTestedClass: NotToBeTestedClass) {}
sayHello() {
return this.notToBeTestedClass.sayHello();
}
}
describe("モック系", () => {
test("ビルトインメソッドをモックできる", () => {
const spy = vitest.spyOn(Math, "random").mockImplementation(() => 0.1);
const value = Math.random();
expect(spy).toBeCalled();
expect(value).toBe(0.1);
});
test("テスト対象じゃないクラスのメソッドをモック化できる", () => {
const notToBeTestedClass = new NotToBeTestedClass();
const toBeTestedClass = new ToBeTestedClass(notToBeTestedClass);
const spy = vitest
.spyOn(toBeTestedClass, "sayHello")
.mockImplementation(() => "Hi");
const value = toBeTestedClass.sayHello();
expect(spy).toBeCalled();
expect(value).toBe("Hi");
});
});
テスト用の環境変数を設定する
$ npm i @dotenvx/dotenvx
vitest.config.ts
/// <reference types="vitest" />
import { defineConfig } from "vite";
+ import dotenvx from "@dotenvx/dotenvx";
export default defineConfig({
+ test: {
+ env: dotenvx.config({ path: ".env.test" }).parsed,
+ },
});
.env.test
PORT=8080
上記の環境変数の例だとテストコード側ではprocess.env.PORT
で参照することができる。
テストファイルを 1 つずつ実行させる
Vitest はデフォルトでテストファイルを並列して実行する。 このためリソースの奪い合いでエラーが起きたりする。
fileParallelism
をfalse
にすることでテストファイルを 1 つずつ実行させることができる。
vitest.config.ts
/// <reference types="vitest" />
import { defineConfig } from "vite";
export default defineConfig({
test: {
+ fileParallelism: false,
},
});