メインコンテンツにスキップ

Jestユニットテスト: JavaScriptチームのための実践ガイド

Jestユニットテストを学び、セットアップからCIまで。モック、TypeScript、カバレッジ、JavaScriptおよびCapacitorアプリケーションのためのベストプラクティスをカバーするハンズオンチュートリアル。

Jestユニットテスト: JavaScriptチームのための実践ガイド

A Capacitor release can pass its end-to-end checks and still ship a broken invoice calculation, stale feature flag, or platform-specific branch. The failure often starts earlier: a unit test still reflects the old behavior, a mock hides a changed dependency, or CI runs a different environment from the developer’s machine. By the time the bug reaches a phone or Electron desktop build, the test suite has provided confidence without providing protection.

それで Jest単位テスト は、単なる実行コマンドとしてではなく、生きたワークフロー決定として扱うべきです。役に立つ質問は、実用的なものです: 開発者は失敗を信頼できる速度で、どの境界が孤立するべきか、CIでどのカバレッジが含まれるべきか、Jestがプロジェクトのモジュールシステムとフィードバックの期待に合致しているかどうか、というものです。このガイドでは、Nodeサービス、Webアプリケーション、Capacitor プロジェクト、Electronアプリケーションを横断するこの決定について説明します。より広いコンテキストについては、自動チェックが配達にどのように組み込まれるかについての説明を参照してください。 自動テストの現代的なソフトウェアワークフロー.

タイトル: なぜJestがまだ重要なのか、ワークフロー決定、古いテストバグ、現代的なテスト戦略を示すインフォグラフィック。

目次

2026年でもJestユニットテストはまだ重要な理由

Jestは、単にアサーションシンタックスを解決するのではなく、チームに、ビジネスロジックを確認するための繰り返し可能な場所、依存性の境界を制御する、カバレッジの期待を強制する、そして、ユーザーに到達する前にモバイルまたはデスクトップパッケージを実行する前にチェックを実行するためのフローを提供する。 これらのワークフローは、ブラウザシェル、Capacitor WebView、Electronレンダラー、Nodeプロセス、プラットフォームAPIの周りで異なるJavaScriptの動作が実行される場合に重要です。

Jestの採用にも歴史的重みがあります。Facebookは 2011 でJavaScriptチャットのリライト用に作成し、 2014でオープンソース化し、OpenJS Foundationは、 38,000 GitHub __CAPGO_KEEP_1__ (, 2022年月一个有会、二七十三月分速。, 上一个。 43,000 __CAPGO_KEEP_0__ 字会、二七十三月分速。 (エアシアコンターバーストログのバーストログにかられにきる。バーストログであるといますだ。バーストログはいますだ。バーストログにいますだ。

バーストログにいますだ。バーストログはいますだ。バーストログにいますだ。

バーストログはいますだ。バーストログにいますだ。バーストログにいますだ。 バーストログにいますだ。バーストログはいますだ。バーストログにいますだ。

バーストログはいますだ。バーストログにいますだ。バーストログにいますだ。

バーストログにいますだ。バーストログはいますだ。バーストログにいますだ。

最小の構成から始めましょう。Nodeサービスは通常、Jestのデフォルト環境とテストスクリプトが必要です。ブラウザ向けのCapacitorまたはElectronモジュールはDOMのようなグローバル変数が必要です。TypeScriptは、デバッグ、モジュールの互換性、起動動作に影響を与える可能性がある変換の決定を追加します。

Nodeプロジェクトの場合、パッケージを初期化し、Jestを開発依存としてインストールしてください。

npm init -y
npm install --save-dev jest
npx jest --init

生成された構成は、設計の最終判断ではありません。テスト環境、変換、モジュールエイリアス、セットアップファイルを確認し、コミットする前に確認してください。

TypeScriptの変換を意図的に選択してください。

ts-jest 既存のリポジトリがTypeScriptコンパイラの動作に依存し、開発者が熟知の診断を望む場合、便利です。 @swc/jest トランスパイルの速度が重要な場合、または型チェックが別のコマンドとして実行されている場合、魅力的です。どちらのオプションも型チェッカを置き換えません。ESM重視のパッケージは、どの変換器でも追加の構成が必要になる可能性があります。

オプション Node TypeScript Capacitor/Electron
環境 node node またはプロジェクト固有 jsdom DOM向けのcode
変換 通常は何もありません ts-jest または @swc/jest TypeScript変換プラスDOMセットアップ
ESMの取り扱い パッケージ形式のマッチ トランスフォーマーへのサポートの検証 プラグインの依存関係とモジュールエイリアスの確認
通常のセットアップ 最小限 jest.config.ts setupFilesAfterEnvモック、ブラウザAPI

A TypeScript configuration using ts-jest can look like this:

import type { Config } from 'jest'

const config: Config = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
  clearMocks: true,
  collectCoverageFrom: ['src/**/*.{ts,tsx}'],
}

export default config

For JavaScript or mixed repositories using Babel, keep the Babel file explicit:

module.exports = {
  presets: [
    ['@babel/preset-env', { targets: { node: 'current' } }],
    '@babel/preset-typescript',
  ],
}

Add only the browser shims your code needs

Capacitor and Electron tests frequently import code that expects window.matchMedia or IntersectionObserverand Electron tests frequently import modules that expects

Object.defineProperty(window, 'matchMedia', {
  writable: true,
  value: (query: string) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: () => {},
    removeListener: () => {},
    addEventListener: () => {},
    removeEventListener: () => {},
    dispatchEvent: () => false,
  }),
})

class MockIntersectionObserver {
  observe() {}
  unobserve() {}
  disconnect() {}
}

Object.defineProperty(window, 'IntersectionObserver', {
  writable: true,
  value: MockIntersectionObserver,
})

If an ESM-only dependency fails during collection, inspect transformIgnorePatterns and the package’s published format. Capacitor plugins can expose this problem when Jest ignores a dependency that still needs transformation. Jest’s newer releases have improved startup and memory behavior, but watch feedback can still trail ESM-focused alternatives in large projects (contextCapacitorライブアップデートの代替手段 experimentalVMModules Appflowの比較/移行マーケティングコピーの場合、Role:短いUIラベルまたはナビゲーションアイテム。見つける場所:page ionic-appflow.astro, page ionic-enterprise-plugins.astro, page solutions/ionic-enterprise-plugins.astro。メッセージキー `appflow_plugins_or` (Appflow Plugins Or)。

JavaScriptに焦点を当てたセットアップウォークスルー用に使用します。 CapgoのJavaScriptのユニットテストガイドを使用します。. 実際の検証コマンドでインストールを完了します:

npx jest --runInBand

Smokeテストがパスすると、Jestがプロジェクトを読み込むことが確認されます。ただし、生産モジュールとテストモジュールのグラフが同一であることを確認するわけではありません。したがって、ESM、DOM、プラグインのインポートテストを、パスが関係する場合に実行してください。

最初の信頼できるユニットテストを書く

有用なユニットテストは、制御されたコンテキストで観察可能な動作を説明します。 アレンジ、実行、確認 パターンはその意図を明確にします: 入力と依存関係を準備し、公開された関数を呼び出し、結果または外部に可視化される効果を検証します。

請求書モジュールがこの関数をエクスポートしている場合を想定します。

export function calculateInvoiceTotal(
  subtotal: number,
  taxRate: number,
  discountRate: number,
): number {
  const discounted = subtotal * (1 - discountRate)
  return Math.round(discounted * (1 + taxRate) * 100) / 100
}

テストは財務的な動作に焦点を当て、ローカル変数名である discounted:

import { calculateInvoiceTotal } from './calculateInvoiceTotal'

describe('calculateInvoiceTotal', () => {
  it('applies percentage discount before tax', () => {
    const subtotal = 100
    const taxRate = 0.2
    const discountRate = 0.1

    const total = calculateInvoiceTotal(subtotal, taxRate, discountRate)

    expect(total).toBe(108)
  })

  it('rounds the final amount to currency precision', () => {
    const total = calculateInvoiceTotal(19.99, 0.2, 0)

    expect(total).toBe(23.99)
  })
})

それぞれ it 1つの動作を表します。後でリファクタリングが内部計算を変更した場合でも、契約を維持する限り、これらのテストは有用なままです。

A list graphic outlining four essential steps for writing reliable unit tests in software development.

Async tests need the same discipline. Jest’s resolvesrejects make the expected promise outcome explicit:

it('returns an invoice from the API', async () => {
  await expect(fetchInvoice('invoice-123')).resolves.toMatchObject({
    id: 'invoice-123',
  })
})

it('rejects when the invoice is missing', async () => {
  await expect(fetchInvoice('missing')).rejects.toThrow('Invoice not found')
})

The dangerous mistake is creating a rejected expectation without awaiting or returning it. Jest-focused guidance identifies forgotten await または return The following three habits produce fragile confidence:Testing private helpers:Test the exported behavior unless the helper represents a meaningful public boundary.

Test the exported behavior unless the helper represents a meaningful public boundary.

  • Test the exported behavior unless the helper represents a meaningful public boundary. Test the exported behavior unless the helper represents a meaningful public boundary.
  • 内部状態の確認: 戻り値、発行されたイベント、永続化されたレコード、または表示される出力を優先してください。
  • 呼び出し確認を多用すること: toHaveBeenCalled() 単に呼び出し確認だけでは何も示されません。関連する引数と結果の動作を確認してください。

PR チェックリストは短く残すことができます:

  • 各テストは 1 つの動作をカバーしているか?
  • テストは Arrange、Act、Assert の順に進んでいるか?
  • 非同期の期待値は待機されているか?
  • 依存関係は明確な境界でモックされているか?
  • テストは内部のリファクタリングに耐えられるか?

コンポーネント固有の例の場合、 CapgoのReactユニットテストガイド Mocking Strategies That Actually Scale

Mocking becomes difficult when a suite grows because every shortcut creates a maintenance obligation. A hand-written can be exactly right for a callback. A module replacement can isolate an __CAPGO_KEEP_0__. A network interceptor can preserve more of the application’s real request behavior. The choice should follow the seam you’re testing.

Consider a payment validator that calls a Stripe __CAPGO_KEEP_0__, records an audit event, and reaches an HTTP risk service. A focused unit test might spy on the logger, replace the payment __CAPGO_KEEP_1__, and intercept the risk request. Each technique controls a different boundary. jest.fn() can be exactly right for a callback. A module replacement can isolate an SDK. A network interceptor can preserve more of the application’s real request behavior. The choice should follow the seam you’re testing.

Consider a payment validator that calls a Stripe SDK, records an audit event, and reaches an HTTP risk service. A focused unit test might spy on the logger, replace the payment SDK, and intercept the risk request. Each technique controls a different boundary.

Fidelity Maintenance burden Best fit or or
jest.fn() Low jest.spyOn() Mocking becomes difficult when a suite grows because every shortcut creates a maintenance obligation. A hand-written can be exactly right for a callback. A module replacement can isolate an __CAPGO_KEEP_0__. A network interceptor can preserve more of the application’s real request behavior. The choice should follow the seam you’re testing. 集中 ローカル時は低 コールバック、ロガー、インジェクトされたサービス
jest.mock() 低から中 急速に成長する 高コストのサイドエフェクトを持つSDK、モジュール
MSW HTTP境界で高 集中化 リクエストの振る舞い、エラー、レスポンス契約

ローカル決定のための手動スパイ

依存性がすでにインジェクションされている場合、テストが1つの相互作用を観察または制御する必要がある場合にスパイを使用してください:

const audit = {
  record: jest.fn(),
}

const result = await validatePayment(input, {
  paymentClient,
  audit,
})

expect(audit.record).toHaveBeenCalledWith(
  expect.objectContaining({ event: 'payment.validated' }),
)
expect(result.status).toBe('approved')

ケース間で状態をリセットする。共有状態はJestのフラッキーフェイルの一般的な原因であり、ガイドラインではモッククリアリングを組み合わせて呼び出し数と状態の漏洩を防ぐことを推奨しています (« beforeEach Jestユニットテストのマスター重量級のSDK用モジュールモック).

Stripeまたはネイティブプラグインモジュールは、ロードしたときにすぐにセットアップを実行します。生産実装をロードする際に、資格情報、ネイティブバインディング、または無関係な動作を導入することは避けます:

アプリケーションから返された値を確認するテストは、意味のある結果を持たないパッシング__CAPGO_KEEP_0__コールアサーションだけでは、モックが設定されたことを証明するだけです。

jest.mock('stripe', () => ({
  payments: {
    authorize: jest.fn(),
  },
}))

The test should still assert the value returned by the application. A passing SDK call assertion without a meaningful result can prove only that the mock was configured.

リクエストの構築、レスポンスのパース、リトライ、エラーの翻訳など、__CAPGO_KEEP_0__が所有する場合、MSWはHTTP層をキャッチしながらリクエストパスを保存できます:

For code that owns request construction, response parsing, retries, or error translation, MSW can intercept the HTTP layer while preserving the request path:

server.use(
  http.post('/risk/check', async () => {
    return HttpResponse.json({ decision: 'review' })
  }),
)

通常、MSWはJestのユニットテストのためのより良い選択肢です。 fetch MSWは、Jestのユニットテストのためのより良い選択肢です。

モックはセームではなくセーム自体に

モッキングが多すぎると、統合のワイヤリングが破綻しているのにテストがパスすることになる。モッキングが少なすぎると、実際のネットワーク、ファイルシステム、時刻、データベースがユニットテストに組み込まれ、スローで非決定論的なスイートが生じる。純粋な論理をすべてのI/Oから解放し、境界の検証を契約または統合テストに移行し、インターフェイスが重要である場合にのみ実行する。

JestをCIとカバレッジゲートと統合する

CIは2つの異なる質問に答えるべきである。最初は、高速なユニットスイートが安全な変更を拒否するかどうか。2番目は、重要な境界がまだ機能するかどうかを確認するための遅い統合チェックが実行されるかどうか。すべてのテストを1つの統一されたコマンドにすると、フィードバックを解釈するのが難しくなり、開発者がスイートを回避するようになる。

Nodeランタイムを固定するGitHubアクションワークフローは、依存関係のキャッシュにロックファイルを使用し、JestのCIモードでユニットチェックを実行できます。

name: test

on:
  pull_request:
  push:

jobs:
  unit:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node: [20, 22]
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
          cache: npm
          cache-dependency-path: package-lock.json

      - run: npm ci
      - run: npm run test:unit, --ci --coverage

      - uses: actions/upload-artifact@v4
        with:
          name: coverage-${{ matrix.node }}
          path: coverage/

パッケージスクリプトは、高速テストと統合ワークを分離できます。

{
  "scripts": {
    "test:unit": "jest --runInBand tests/unit",
    "test:integration": "jest --runInBand tests/integration"
  }
}

カバレッジの閾値をリスクに基づいて設定する。普遍的な数字を習慣的に選択するのではなく。

module.exports = {
  collectCoverageFrom: ['src/**/*.{js,ts,tsx}'],
  coverageThreshold: {
    global: {
      branches: 70,
      functions: 80,
      lines: 80,
      statements: 80
    }
  }
}

その値は、設定の例であり、業界標準として検証されていないものです。実際のフロアをリポジトリの現在のベースラインから設定し、チームが意味のあるカバレッジを追加するたびに上げる。厳格なゲートは、生成されたものや低リスクのcodeを測定することで、緊急修正をブロックする可能性があります。ゆるやかなゲートは、重要なパスが気づかれずに劣化する可能性があります。

https://docs.github.com/assets/images/help/repository/actions-illustration.pngからスクリーンショット

大規模リポジトリの場合、テスト分離が確実な後で、行列ベースのシャーディングを使用してください。各シャードには、報告の明確な所有権が必要であり、最終的なステータスは、ログに埋もれさせるのではなく、プルリクエストで失敗を表示するようにしてください。 lcov データをCodecovまたはCoverallsにアップロードすることもできます。ただし、サービスが報告を正しくマージするように設定されている必要があります。

読む CI運用慣行 alongside your workflow design, especially if multiple applications share one repository. Capgo’s __CAPGO_KEEP_0__のCI/CD統合テストガイド CI/CD統合テストガイド

大規模なJestスイートは、常にパスすることができますが、間違ったことをテストしている可能性があります。テストが実装詳細に依存している場合、信頼性が低下します。スナップショットが有効なレビューの範囲を超え、共有モックがテストが設定したものとは異なる動作を引き起こす場合も同様です。

スナップショットには、意図的な所有権が必要です。構造が実際の契約である場合、例えば安定したコンポーネントまたはシリアライズされたメッセージの場合、スナップショットは効果的です。開発者が広範な更新を承認する際に、出力を検査せずにスナップショットがノイズを生み出す場合も同様です。意図的に再生成し、差分をレビューし、意味のある動作を保護するファイルを削除してください。

レイヤーを使用するのではなく、Jestが全てを管理するのを強制しない

各テストレイヤーには、狭いジョブが必要です:

]}

  • 単体テスト: ネットワークやファイルシステムへのアクセスなしで、決定論的計算、パーサー、削減、ポリシー決定、エラーのマッピングを検証する。
  • 契約テスト: モジュールの境界、エディタの形状、リクエストのペイロード、プラグイン向けの動作を確認する。
  • 薄いエンドツーヨーロードカバレッジ: Web、Capacitor、またはElectronシェル上で、実際のユーザージャーニーを少数実行する。

この配置により、単体テストが速くなり、モックが生産を表すのを止める境界をチェックする。さらに、UIパスを経由して、コストのかかるネイティブのアップデートやパーミッションフローをテストするのではなく、JavaScriptの決定論的ロジックを分離することで、モバイルの一般的な失敗モードを回避する。

1つの動作ごとに it() 失敗が診断可能なので、1つの動作ごとにテストを実行する。アサートの呼び出しの順序は、契約に属する場合にのみチェックする。記憶域にメソッドを公開し、保存されたレコードを露出するような、クエリ可能な偽物は、現在の実装をコピーするだけのハードコードされた戻り値よりも、より良いフィードバックを提供する。

パスするテストは、ユーザーや隣接するモジュールが依存できることを説明するべきであり、今日のcodeがどのように構成されているかを説明するべきではない。

定期的なテストヘルスレビューをスケジュールする。フレイクテスト、古いスナップショット、冗長なセットアップ、モックが生産の動作と一致しない場合を確認する。低価値のテストを削除することは、すでに隠蔽しているテストに別のアサートを追加することよりも安全である。

CI ダッシュボードでフラッキネスを追跡する。テストが失敗した場合に code の変更がない場合は、失敗の証拠を保存し、共有状態を分離し、時間とランダム性を制御し、ランナーがオーバーロードされている場合にワーカーの圧力を減らす。CI マシン上の測定値に基づいてワーカーの制限を設定する必要があります。追加の並行性は競合を増やし、スイートの実行速度を低下させる可能性があるためです。ワーカーの設定は、ランナーの構成とともにドキュメント化する必要があります。将来の変更が意図的なものであることを保証するためです。

チームのための決定枠組みと次のステップ

Jest は、リポジトリがすでに安定したスイート、established の変換と偽装、そしてチームがランナー実験よりもマイグレーション安全性を優先する場合、信頼できるデフォルトです。ESM-ファーストの新しいプロジェクト、ネイティブのフィードバックが優先される場合、または watch-mode の再実行が開発を頻繁に妨げる場合に、代替品を比較検討することをお勧めします。公開された比較結果は、リポジトリのアーキテクチャとトランスフォーマーの作業によって大きく異なる可能性があるため、方向性として扱うことができます。

4 つの基準を使用します。

  1. 既存のツール: Jest を維持する必要があります。既存の構成、テスト ユーティリティ、および CI の慣習が機能している場合。
  2. モジュール形式: ESM-オンリーの依存関係が繰り返し例外やカスタムワークアラウンドを必要とする場合、ランナーを再検討する必要があります。
  3. フィードバックの期待: 実際のリポジトリで代表的な watch 変更を測定する必要があります。空のデモプロジェクトではありません。
  4. チームの能力: チームが構成およびデバッグできる熟練したランナーが、速度が速いが不適切に使用されるツールよりも良好な結果を生み出す可能性があります。

Vitest、Nodeの組み込みテストランナー、Playwrightは異なるニーズを対処します。Playwrightは主にブラウザのE2Eカバレッジに適しています。Nodeのランナーは、焦点を当てたNodeサービスに適しています。VitestはしばしばグリーンフィールドのESMとVite向けのプロジェクトに適しています。Jestは、成熟したモック、変換、既存のCI規約に依存するチームに適しています。信頼できる境界を維持しながら、フィードバックが使用可能なものになるように、ランナーを選択してください。

実践的な90日間のリセット

第1月、スイートを検査します。 所有者を割り当てて、フラッキーテスト、死んだスナップショット、実装に結びついたアサーション、実行中のI/Oを実行するテストをカタログ化します。出力は、削除、修理、統合カバレッジが必要な境界の書かれたリストでなければなりません。

第2月、設計を標準化します。 共有テストユーティリティを追加し、モッキング規約をドキュメント化し、重要なパッケージのカバレッジレポートを導入します。ゲートを持つパッケージと、焦点を当てたテストが不足している動作を記録してください。基準はレビュー可能なものでなければなりません。

第3月、配信を安定化します。 CIワーカーを調整し、単位と統合ジョブを分離し、リスクベースのカバレッジフロアを設定し、規約をドキュメント化してください。パイプラインは、実行可能な失敗を報告し、新しいテストは同じ境界とモッキング規約に従うようにしてください。 TESTING.md第3月、配信を安定化します。

定期スケジュールでスイートをレビュー。古いスナップショット、冗長なセットアップ、および生産環境の動作に一致しないモックを削除します。低価のテストは、もう一つのアサーションで強化するのではなく、削除することの方が安全です。CIでフレイキーフェイルを追跡し、証拠を保存し、共有状態を分離し、時間とランダム性を制御し、競合が発生したときにワーカーの圧力を減らします。CIマシンでの測定値に基づいてワーカーの制限を設定し、ランナー構成と共にドキュメントを保存します。

これらの慣行は、より広範な ソフトウェア開発のベストプラクティス. For a tested JavaScript fix targeting Capacitor or Electron users, Capgo can deliver signed JavaScript, CSS, configuration, and asset bundles through targeted channels, with rollback protection and release observability, without requiring a store review for every web-layer correction.

If your team ships Capacitor or Electron applications, visit Capgo to assess how live JavaScript updates fit controlled channels, staged rollouts, and rollback protection. Start by documenting Jest boundaries and CI gates, then define where Capgo belongs in the recovery path for fixes that need prompt delivery.

リアルタイム更新用のCapacitorアプリ

When a web-layer bug is live, ship the fix through Capgo instead of waiting days for app store approval. Users get the update in the background while native changes stay in the normal review path.

ウェブ層のバグが実行中の場合、__CAPGO_KEEP_0__を通して修正を配信するのではなく、数日間待ってアプリストアの承認を待つのではなく、ユーザーはバックグラウンドで更新を受け取り、ネイティブの変更は通常のレビュー経路を通る。

コンテキスト: Capgoマーケティングウェブサイト。役割: サポートする説明文またはメタ説明文。見つける場所: コンポーネント GetStarted.astro。Capgo製品/ブランドと開発者用語をそのまま保存する。メッセージキー `instant_updates_for_capacitor_apps_description` (Capacitorアプリのリアルタイム更新の説明)。

マーティンから人間のサポート

Capgo gives you the best insights you need to create a truly professional mobile app.