Unit Test vs Integration Test
iOSテスト戦略の基盤: 単体を分離するユニットテストか、コンポーネント間のやり取りを検証する統合テストか。テストピラミッドを正しく構築する方法とは?
Vite自身の速さで動く、ネイティブESM/TSテストランナー
13年の歴史を持つ成熟したフレームワークで、React Nativeのデフォルト
ViteまたはNuxtベースの新規プロジェクトでは、デフォルトをVitestにすべきだ:同じ`vite.config.js`を共有できる。Next.jsではconfigは別扱いになる(コンパイラがViteではないため)が、ネイティブなESM/TypeScript対応を追加のtransformパッケージなしで得られる。 ただし、稼働中の大規模なJestスイートがあり、特にRN/Metroに依存している場合、移行コストが利益を上回ることがある。第三の道を選ぶとよい:新しいテストはVitestで書き、既存のスイートはJestに残す — 両者は同じモノレポ内で共存できる。
| カテゴリー | Vitest 5 | Jest |
|---|---|---|
| パフォーマンス | 9/10 | 7/10 |
| 学習のしやすさ | 7/10 | 7/10 |
| エコシステム | 6/10 | 9/10 |
| コミュニティ | 6/10 | 9/10 |
| 求人市場 | 6/10 | 8/10 |
| 将来性 | 9/10 | 6/10 |
// Vitest 5 - カート合計を計算する関数のテスト
// vitest.config.tsはvite.config.tsを共有するため、追加のtransformは不要
import { describe, it, expect, vi } from 'vitest'
import { calculateCartTotal } from './cart'
import { fetchDiscount } from './discount-api'
vi.mock('./discount-api')
describe('calculateCartTotal', () => {
// Vitest 5: clearMocksはデフォルトでtrue、追加のクリーンアップ呼び出しは不要
it('indirim kodu olmadan ara toplami dogru hesaplar', () => {
const items = [
{ price: 129.9, qty: 2 },
{ price: 49.5, qty: 1 },
]
expect(calculateCartTotal(items)).toBeCloseTo(309.3, 2)
})
it('gecerli indirim kodunda vi.when ile kosullu mock kullanir', async () => {
// Vitest 5の新API: 引数に応じて異なるmock動作
vi.when(fetchDiscount).calledWith('SEPET10').thenResolve({ percent: 10 })
vi.when(fetchDiscount).calledWith('GECERSIZ').thenReject(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
const total = await calculateCartTotal(items, 'SEPET10')
expect(total).toBeCloseTo(90, 2)
expect(fetchDiscount).toHaveBeenCalledWith('SEPET10')
})
it('gecersiz kod hata firlatir', async () => {
vi.when(fetchDiscount).calledWith('GECERSIZ').thenReject(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
await expect(calculateCartTotal(items, 'GECERSIZ')).rejects.toThrow('invalid code')
})
})
// モノレポ: vitest -p web --coverage('web'プロジェクトのみ + カバレッジレポート)// Jest 30 - カート合計を計算する関数のテスト
// babel.config.jsまたはts-jestのtransformチェーンが別途セットアップされている必要がある
const { calculateCartTotal } = require('./cart')
const { fetchDiscount } = require('./discount-api')
jest.mock('./discount-api')
describe('calculateCartTotal', () => {
afterEach(() => {
jest.clearAllMocks() // Jestではデフォルトではないため、手動で呼び出す
})
it('indirim kodu olmadan ara toplami dogru hesaplar', () => {
const items = [
{ price: 129.9, qty: 2 },
{ price: 49.5, qty: 1 },
]
expect(calculateCartTotal(items)).toBeCloseTo(309.3, 2)
})
it('gecerli indirim kodunda mockResolvedValue kullanir', async () => {
fetchDiscount.mockImplementation((code) => {
if (code === 'SEPET10') return Promise.resolve({ percent: 10 })
return Promise.reject(new Error('invalid code'))
})
const items = [{ price: 100, qty: 1 }]
const total = await calculateCartTotal(items, 'SEPET10')
expect(total).toBeCloseTo(90, 2)
expect(fetchDiscount).toHaveBeenCalledWith('SEPET10')
})
it('gecersiz kod hata firlatir', async () => {
fetchDiscount.mockRejectedValue(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
await expect(calculateCartTotal(items, 'GECERSIZ')).rejects.toThrow('invalid code')
})
})
// jest --projects packages/web --coverage で、モノレポ内の'web'パッケージのみを実行ViteまたはNuxtベースの新規プロジェクトでは、デフォルトをVitestにすべきだ:同じ`vite.config.js`を共有できる。Next.jsではconfigは別扱いになる(コンパイラがViteではないため)が、ネイティブなESM/TypeScript対応を追加のtransformパッケージなしで得られる。 ただし、稼働中の大規模なJestスイートがあり、特にRN/Metroに依存している場合、移行コストが利益を上回ることがある。第三の道を選ぶとよい:新しいテストはVitestで書き、既存のスイートはJestに残す — 両者は同じモノレポ内で共存できる。
無料相談を受けるいいえ、1対1で置き換わるわけではない。両方とも2026年9月時点で活発に開発が続いている:Vitest 5.0(2026年9月3日)とJest 30.5.2(2026年9月18日)。ViteおよびNuxtベースのプロジェクトでは、Vitestがアプリケーション自身のtransformパイプラインを再利用するためセットアップの負担が少ない。一方Jestは特にReact Native/Metroや既存の大規模コードベースで引き続き使われ続けている。