Vitestでブラウザテストができる!? Browser Modeを試してみた
久々に Vitest のドキュメントを漁ったら experimental 段階ですが新しい機能が追加されていました。
昨今のフロントエンド界隈のエコシステムが更新されるスピードは早いですね。
今回は Vitest の実験的(experimental)な新しいテスト方法である Browser Mode を触っていきます。
環境構築
Vite ベースのプロジェクト作成
pnpm create vite@latest playground-vitest-browswer
✓ Select a framework: › React
✓ Select a variant: › TypeScript + SWC
※後で記載していますが、Vitest Browser Mode の使用時には TypeScript
を選択せずに TypeScript + SWC
を使用するとテスト時に ReferenceError: React is not defined
が発生しテストが通らないので注意してください。
Vitest のインストール
pnpm i -D vitest
Browser Mode のインストール
pnpx vitest init browser
インタラクティブ形式で表示される設問を選びます。
✓ Choose a language for your tests › TypeScript
✓ Choose a browser provider. Vitest will use its API to control the testing environment › playwright
✓ Choose a browser › chromium
✓ Choose your framework › react
✓ Install Playwright browsers (can be done manually via ‘pnpm exec playwright install’)? … yes
Choose a browser provider の設問では 3つの選択肢が表示され、browser provider には playwright
webdriverio
preview
から選択可能ですが、preview
は CI に適していないと注意書きがあるため playwright
を選択しました。
preview – Preview is useful to quickly run your tests in the browser, but not suitable for CI.
DeepL 翻訳:プレビューは、ブラウザ上でテストを素早く実行するのに便利だが、CI には適していない。
pnpx vitest init browser
■ This utility will help you set up a browser testing environment.
✓ Choose a language for your tests › TypeScript
✓ Choose a browser provider. Vitest will use its API to control the testing environment › playwright
✓ Choose a browser › chromium
✓ Choose your framework › react
✓ Install Playwright browsers (can be done manually via 'pnpm exec playwright install')? … yes
■ Installing packages...
■ @vitest/browser, vitest-browser-react, playwright, @vitejs/plugin-react
devDependencies:
+ @vitejs/plugin-react 4.3.2
+ @vitest/browser 2.1.2
+ playwright 1.47.2
+ vitest-browser-react 0.0.1
Done in 2.8s
✓ Created a workspace file for browser tests: vitest.workspace.ts
✓ Added "test:browser" script to your package.json.
■ Installing Playwright dependencies with `pnpx playwright install --with-deps`...
■ Add "@vitest/browser/providers/playwright" to your tsconfig.json "compilerOptions.types" field to have better intellisense support.
✓ Created example test file in vitest-example/HelloWorld.test.tsx
You can safely delete this file once you have written your own tests.
■ All done! Run your tests with pnpm test:browser
Vitest の設定
Browser Mode の設定はインストール時に自動的に行なってくれますが、TypeScript の型周りの設定は追加する必要があるようです。
インストール時に追加されるファイル
.
├── vitest-example/
│ ├── HelloWorld.test.tsx
│ └── HelloWorld.tsx
└── vitest.workspace.ts
参照:https://vitest.dev/guide/browser/#configuration
ドキュメントを参考に TypeScript の型定義の追加を行います。
Browser Mode のインストール後に表示される案内に従って tsconfig.app.json に TypeScript の型定義を追加します。
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -18,7 +18,9 @@
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true
+ "noFallthroughCasesInSwitch": true,
+
+ "types": ["@vitest/browser/providers/playwright"]
},
"include": ["src"]
}
compilerOptions.include
プロパティには src しか設定されていないので、このままでは vitest-example ディレクトリ内のファイルは TypeScript に認識されません。
src ディレクトリ内に移動しておきましょう。
mv vitest-example src/
ついでに Vitest の設定でグローバル API を有効化します。
参照:https://vitest.dev/config/#globals
Browser Mode のインストールで vitest.workspace.ts ファイルが作成されるのでそちらに追記します。
--- a/vitest.workspace.ts
+++ b/vitest.workspace.ts
@@ -13,6 +13,7 @@ export default defineWorkspace([
// https://playwright.dev
providerOptions: {},
},
+ globals: true,
},
},
]);
グローバル API の有効化に合わせて tsconfig.app.json に TypeScript の型情報を追加します。
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -20,7 +20,7 @@
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
- "types": ["@vitest/browser/providers/playwright"]
+ "types": ["@vitest/browser/providers/playwright", "vitest/globals"]
},
"include": ["src"]
}
閑話休題: vitest.workspace.ts とはなんぞや
これまで vite.config.ts
や vitest.config.ts
で Vitest の設定を行なっていたのですが、今回見慣れないファイルが出てきたので調べました。
いつの間にか Vitest に monorepo のビルトインサポートが入ったみたいですね。
特筆すべきはディレクトリやファイル名によってユニットテストやインテグレーションテストを分けられる点です。
import { defineWorkspace } from "vitest/config";
export default defineWorkspace([
{
extends: "./vitest.config.ts",
test: {
name: "unit",
include: ["**/*.unit.test.ts"],
},
},
{
extends: "./vitest.config.ts",
test: {
name: "integration",
include: ["**/*.integration.test.ts"],
},
},
]);
参照:https://vitest.dev/guide/workspace#configuration
実際の現場では jsdom(happy-dom)がサポートしている DOM のエミュレートだけではカバーできずにモックを使用したり、実際のブラウザとは環境が違うことによるテストのしにくかったりという課題があります。
実行速度が早いユニットテストや jsdom(happy-dom)でカバーできるインテグレーションテストと、実際のブラウザを使用するブラウザテストを両立できるようになるのは非常によさそうです。
初めての Browser Mode テスト
Browser Mode のインストール時に vitest-example
ディレクトリと package.json
に npm scripts が追加されています。
{
...
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
+ "test:browser": "vitest --workspace=vitest.workspace.ts"
},
...
}
// HelloWorld.tsx
export default function HelloWorld({ name }: { name: string }) {
return (
<div>
<h1>Hello {name}!</h1>
</div>
);
}
// HelloWorld.test.tsx
import { expect, test } from "vitest";
import { render } from "vitest-browser-react";
import HelloWorld from "./HelloWorld.jsx";
test("renders name", async () => {
const { getByText } = render(<HelloWorld name="Vitest" />);
await expect.element(getByText("Hello Vitest!")).toBeInTheDocument();
});
手始めにテストを実行してみます。
pnpm test:browser
oh…
原因は Vite ベースのプロジェクト作成時に @vitejs/plugin-react-swc
を選択していたことでした。
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,5 +1,5 @@
import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react-swc";
+import react from "@vitejs/plugin-react";
// https://vitejs.dev/config/
export default defineConfig({
こちらに変更して再度テストを実行するとテストが通るようになりました。
せっかくなので jsdom(happy-dom) ではできないようなテストも書いてみます。
jsdom では ResizeObserver がサポートされていないので、 ResizeObserver を使ってドキュメントの width と height を出力するコンポーネントを作成します。
// App.tsx
import { useEffect, useRef, useState } from "react";
import "./App.css";
type Rect = {
readonly width: number;
readonly height: number;
};
function App() {
const ref = useRef<HTMLDivElement>(null);
const [rect, setRect] = useState<Rect | undefined>();
useEffect(() => {
const observer = new ResizeObserver((entries) => {
entries.forEach((entry) => {
setRect({
width: entry.contentRect.width,
height: entry.contentRect.height,
});
});
});
observer.observe(document.body);
return () => {
observer.disconnect();
};
}, []);
return (
<div ref={ref}>
<p>width: {rect?.width}</p>
<p>height: {rect?.height}</p>
</div>
);
}
export default App;
これに対するテストがこちらになります。
// App.test.tsx
// 簡易的にファイル内で import していますが、テスト毎に import が必要になるので vitest.workspace.ts に移動した方がよさそうです
// ref. https://github.com/vitest-dev/vitest-browser-react?tab=readme-ov-file#vitest-browser-react
import "vitest-browser-react";
import { page } from "@vitest/browser/context";
import App from "./App";
test("should be work with ResizeObserver", async () => {
const screen = page.render(<App />);
// デフォルトの viewport は 414x896
// ref. https://vitest.dev/config/#browser-viewport
await expect.element(screen.getByText("width: 414")).toBeInTheDocument();
await expect.element(screen.getByText("height: 896")).toBeInTheDocument();
// viewport を 1920x1080 に変更します
await page.viewport(1920, 1080);
await expect.element(screen.getByText("width: 1920")).toBeInTheDocument();
await expect.element(screen.getByText("height: 1080")).toBeInTheDocument();
});
Vitest Browser Mode のデフォルト viewport は 414×896 なのでまずその確認してから viewport を変更してサイズを確認してます。
これでテストを走らせると
[0] Browser runner started by playwright at http://localhost:5173/
✓ |0| src/App.test.tsx (1)
✓ |0| src/vitest-example/HelloWorld.test.tsx (1)
Test Files 2 passed (2)
Tests 2 passed (2)
Start at 14:50:48
Duration 981ms (transform 0ms, setup 0ms, collect 96ms, tests 169ms, environment 0ms, prepare 31ms)
ResizeObserver を使ってもちゃんと動いていますね。
注意事項
この検証をしている時にブラウザを起動した状態で試すと viewport で指定した width から-15px されてテストが通らなかったため、ヘッドレスモードでテストを行なっています。
恐らくは Vitest Browser Mode の UI で使用される iframe のスクロールバー分の横幅が引かれた状態になってしまうのかな?という予想ですが詳細不明です。
document.body の横幅をテストしたいケースはそこまでないかと思ったので、取り急ぎ ヘッドレスモードで難を凌いでいますのでご注意ください。
--- a/vitest.workspace.ts
+++ b/vitest.workspace.ts
@@ -12,6 +12,7 @@ export default defineWorkspace([
provider: "playwright",
// https://playwright.dev
providerOptions: {},
+ headless: true,
},
globals: true,
},
参照:https://vitest.dev/guide/browser/#headless
まとめ
Vitest を使用してブラウザテストができるのは中々インパクトがあるのではないでしょうか。
公式ドキュメントにもあるように experimental 段階ですので、API に将来変更が入る可能性が高いので導入にはドキュメント等を確認の上ご検討ください。
この記事を書いた人
- 2013年にアーティスに入社。システムエンジニアとしてアーティスCMSを使用したWebサイトや受託システムの構築・保守に携わる。環境構築が好き。
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー