「Reactのテストを書きたいけど、JestとVitestどっちを入れればいいの?」——2026年のこの質問には、はっきり答えが出ています。
結論、今からReactのテストを始めるならVitest + React Testing Libraryです。かつての定番だったcreate-react-app(Jest同梱)は公式に廃止され、Viteで作った環境にはVitestがほぼ設定ゼロで乗るからです。
この記事では、現役フロントエンドエンジニアの私が、Vite + Vitest + Testing Libraryのセットアップから「実際に動くテストコード3例」まで、コピペで再現できる形で解説します。Jestとの違い・移行のしやすさも比較表つきでカバーします。
この記事でわかること
・2026年にJestではなくVitestを選ぶ理由(比較表つき)
・Vite + Vitest + React Testing Libraryの最短セットアップ
・レンダリング/クリック/非同期の動くテストコード3例
・Jestからの移行がほぼ書き換え不要な理由
結論:今から選ぶならVitest。理由は「Viteとの一体感」
JestはReactテストの王者として長く君臨してきましたし、2025年リリースのJest 30で高速化も果たした現役のフレームワークです。
ただし、Jestが「標準」だった最大の理由はcreate-react-app(CRA)に同梱されていたから。そのCRAが廃止された今、新規プロジェクトの多くはViteで作る時代です。
そしてViteプロジェクトにJestを入れるには、変換まわりの設定を自前で組む手間がかかります。
一方のVitestはVite公式ファミリーのテストフレームワーク(2026年7月時点でv4系)。Viteの設定をそのまま共有するため、追加設定が数行で済みます。
しかもテストの書き方(expect API)はJest互換。つまり「Jestで学んだ知識はそのまま、セットアップの苦労だけが消える」わけです。
テストフレームワーク選びは実力の差ではなく、ビルドツールとの相性で決まる——これが2026年の答えです。
| 項目 | Jest(30系) | Vitest(4系) |
|---|---|---|
| Viteとの相性 | 変換設定を自前で用意 | 設定を共有、ほぼゼロ設定 |
| ESM対応 | 対応は進んだが設定が必要な場面あり | ネイティブ対応 |
| テストAPI | describe / test / expect | Jest互換(同じ書き方) |
| モック | jest.fn() / jest.mock() | vi.fn() / vi.mock() |
| 向いている場面 | 既存のJest資産が大きいプロジェクト | Vite製の新規・現行プロジェクト |
セットアップ:Vite + Vitest + Testing Library
ステップ1:Viteでプロジェクトを作成する
npm create vite@latest my-app -- --template react-ts
cd my-app
npm installReact + TypeScriptのテンプレートで作成します。既存のViteプロジェクトがある人はこのステップは不要です。
ステップ2:テスト関連パッケージをインストールする
npm install -D vitest jsdom @testing-library/react @testing-library/dom @testing-library/jest-dom @testing-library/user-eventそれぞれの役割は次のとおりです。
- vitest:テストフレームワーク本体
- jsdom:Node.js上にブラウザ相当のDOMを再現する
- @testing-library/react:Reactコンポーネントを描画・検証する(React Testing Library)。v16からは@testing-library/domのインストールも必須
- @testing-library/jest-dom:
toBeInTheDocument()などDOM向けの便利なマッチャーを追加 - @testing-library/user-event:クリックや入力など「ユーザー操作」を再現
ステップ3:vite.config.tsにtest設定を追加する
/// <reference types="vitest/config" />
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
test: {
globals: true, // test / expect をimportなしで使える
environment: 'jsdom', // DOMを使うコンポーネントテストに必須
setupFiles: './src/setupTests.ts',
},
})ポイントはVitest専用の設定ファイルを作らず、Viteの設定にtestブロックを足すだけという点。Jestでいうjest.config.jsとbabel設定に相当する作業が、この数行で終わります。
ステップ4:セットアップファイルとtsconfigを整える
import '@testing-library/jest-dom/vitest'この1行で、VitestのexpectにtoBeInTheDocument()などのマッチャーが追加されます。さらにTypeScriptに型を教えるため、tsconfig.app.jsonのcompilerOptionsへ次を追加します。
{
"compilerOptions": {
"types": ["vitest/globals", "@testing-library/jest-dom"]
}
}最後にpackage.jsonへテスト用スクリプトを追加すれば準備完了です。
{
"scripts": {
"test": "vitest",
"test:run": "vitest run"
}
}npm run testはファイル変更を監視して自動再実行するwatchモード、npm run test:runはCI向けの1回実行です。
実践:動くテストを3つ書いてみる
テスト対象として、小さなカウンターコンポーネントを用意します。
import { useState } from 'react'
export const Counter = () => {
const [count, setCount] = useState(0)
return (
<div>
<h1>カウンター</h1>
<p>現在の値: {count}</p>
<button onClick={() => setCount((c) => c + 1)}>+1</button>
</div>
)
}例1:レンダリングのテスト(表示されているか)
import { render, screen } from '@testing-library/react'
import { Counter } from './Counter'
test('見出しとボタンが表示される', () => {
render(<Counter />)
expect(screen.getByRole('heading', { name: 'カウンター' })).toBeInTheDocument()
expect(screen.getByRole('button', { name: '+1' })).toBeInTheDocument()
})render()でコンポーネントをjsdom上に描画し、screen.getByRole()で「見出し」「ボタン」という役割(ロール)で要素を探します。Testing Libraryの思想は「ユーザーに見えるものでテストする」。CSSクラスや内部実装ではなくロールで探すのが基本です。
例2:クリックイベントのテスト(操作して変わるか)
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Counter } from './Counter'
test('ボタンを2回押すとカウントが2になる', async () => {
const user = userEvent.setup()
render(<Counter />)
await user.click(screen.getByRole('button', { name: '+1' }))
await user.click(screen.getByRole('button', { name: '+1' }))
expect(screen.getByText('現在の値: 2')).toBeInTheDocument()
})クリックはuser-eventで再現します。user.click()は非同期なので、テスト関数をasyncにしてawaitを付けるのを忘れずに。ここを忘れると「クリックしたのに値が変わらない」という不可解な失敗になります。
例3:非同期処理のテスト(データ取得を待つ)
API取得のような非同期処理を含むコンポーネントもテストしてみましょう。取得関数をpropsで受け取る形にすると、モックしやすく設計としても健全です。
import { useEffect, useState } from 'react'
type Props = {
fetchUserName: () => Promise<string>
}
export const UserName = ({ fetchUserName }: Props) => {
const [name, setName] = useState<string | null>(null)
useEffect(() => {
fetchUserName().then(setName)
}, [fetchUserName])
if (name === null) return <p>読み込み中...</p>
return <p>ようこそ、{name}さん</p>
}import { render, screen } from '@testing-library/react'
import { vi } from 'vitest'
import { UserName } from './UserName'
test('取得したユーザー名が表示される', async () => {
const fetchUserName = vi.fn().mockResolvedValue('太郎')
render(<UserName fetchUserName={fetchUserName} />)
// 取得前はローディング表示
expect(screen.getByText('読み込み中...')).toBeInTheDocument()
// findBy〜は要素が現れるまで待ってくれる
expect(await screen.findByText('ようこそ、太郎さん')).toBeInTheDocument()
})非同期テストの主役はfindBy〜(要素が現れるまで待つ)とvi.fn()(モック関数)の2つ。getBy〜は「今すぐそこにあるはず」、findBy〜は「そのうち現れるはず」と覚えてください。
テストの基本サイクルは「赤→緑」
テストに慣れる一番の近道は、わざと失敗させてから直すことです。たとえば例1のテストで検索文字列を存在しないもの(「カウンタ一覧」など)に変えて実行すると、テストは赤く失敗します。CRA + Jest時代の画面ですが、成功時はこのような表示でした。

失敗させると、どこで・何を期待して・実際は何だったのかが赤字で表示されます。

コードを直して再実行し、緑(PASS)に戻れば1サイクル完了。Vitestでも表示のデザインは違えど、この「赤→緑」のリズムはまったく同じです。watchモードなら保存のたびに自動で再実行されるので、このサイクルが高速に回せます。

Jestからの移行は「思っているより簡単」
すでにJestで書かれたテストがある場合も心配いりません。VitestのテストAPIはJest互換で設計されているため、describe / test / expectまわりは基本そのまま動きます。主な書き換えポイントは次の程度です。
jest.fn()→vi.fn()、jest.mock()→vi.mock()(機械的な置換でOK)- jest.config.jsの内容を、vite.config.tsのtestブロックへ移す
- jest-domのimportを
@testing-library/jest-dom/vitestに変更
現場でよくあるのは「移行工数を恐れて古い構成を引きずる」ケースですが、コンポーネントテスト中心のプロジェクトなら移行は想像よりずっと軽い作業です。逆に、Next.jsプロジェクトでJestを使い続ける選択も現実的で、その手順はNext.js + Jestの導入手順にまとめています。
初心者がハマるポイントとプロのコツ
- ハマりどころ①:
toBeInTheDocument is not a functionエラー。setupTests.tsの読み込み忘れ(vite.config.tsのsetupFiles指定漏れ)が原因の定番です。 - ハマりどころ②:
document is not defined。environmentがjsdomになっていないときのエラーです。 - プロのコツ:要素の取得は
getByTestIdに逃げず、まずgetByRoleを使う。ロールで取れないUIは、たいていアクセシビリティにも問題があります。テストが書きにくいコンポーネントは、設計が悪いというシグナルです。
まとめ:テストが書けるReactエンジニアは希少価値が高い
- 2026年の新規Reactプロジェクトは「Vite + Vitest + React Testing Library」が標準
- 設定はvite.config.tsに数行足すだけ。jsdomとsetupTests.tsを忘れずに
- getByRoleで探し、操作はuser-event、非同期はfindBy〜で待つ
- VitestのAPIはJest互換なので、既存知識も既存テストも無駄にならない
次のアクションは、この記事のCounterのテストを自分の環境で「赤→緑」まで一度回してみること。それだけで景色が変わります。
そして実務の話をすると、未経験・ジュニア層で「テストを書いたことがある」人は本当に少数派です。ポートフォリオにテストコードが入っているだけで、面接での技術的な会話が一段深くなります。独学でここまで到達するのが難しいと感じたら、テストやチーム開発まで教えてくれるスクールで固めるのも近道ですし、テスト経験を職務経歴で語れるようになったら、転職エージェントに市場価値を聞いてみる価値は十分あります。
よくある質問(FAQ)
Q. Jestはもう学ばなくていいですか?
「書き方」はVitestとほぼ共通なので、どちらで学んでも損はありません。既存プロジェクトではJest(現行はJest 30系)が現役で動いているため、読める必要はあります。新規で環境を作るならVitestを選べばOKです。
Q. Next.jsの場合もVitestでいいですか?
Next.jsはVitest・Jestどちらの構成も公式にサポートしています。コンポーネント単体のテストならVitestで問題ありません。Jestで組む場合の手順はNext.js + Jest導入ガイドを参照してください。
Q. どこまでテストを書けばいいですか?
最初から網羅を目指す必要はありません。「バグると困る箇所」——フォームのバリデーション、料金計算、条件分岐の多い表示ロジック——から書くのが費用対効果の高い順番です。まずは1コンポーネント1テストから始めましょう。基礎からReactを固め直したい人はReact入門の学習ロードマップもどうぞ。
あわせて読みたい関連記事
Next.js 16×Jest導入ガイド|App Router対応の最新設定【2026年】Next.js 16のApp RouterでJestを動かす最新手順を解説。next/jestを使えばts-jestは不要で、TypeScriptもそのまま動きます。動くテスト例3つ、Server Componentテストの注意点、Vitestとの比較まで2026年基準でまとめました。frontendlab.magicgifted.com
CRAからVite移行5ステップ|React開発を爆速化【2026年版】create-react-appの起動が遅いのは構造的な問題で、解決策はVite移行一択です。既存Reactプロジェクトを30分でViteに乗せ替える5ステップを、Vite 8時代のNode.js要件やハマりがちな落とし穴とともに2026年基準で解説します。frontendlab.magicgifted.com
ReactにESLint・Prettierを導入する完全ガイド【Vite・TypeScript・ESLint v9対応】Vite + React(TypeScript)にESLint v9とPrettierを導入する2026年版ガイド。flat config、VSCodeの保存時整形、husky・lint-stagedのコミット前チェック、動作確認まで解説します。frontendlab.magicgifted.com



