メインコンテンツにジャンプ

Unit Testing React: A Practical End-to-End Guide

Reactのユニットテストの設定からCI/CDまでマスターする。 このガイドでは、Jest、RTL、フック、非同期code、モッキング、強力なクロスプラットフォームアプリ用のベストプラクティスをカバーします。

マーティン・ドナディュー

マーティン・ドナディュー

マーケティング担当者

Unit Testing React: 実践的なエンドツーエンドガイド

昼食前、UIの小さな変更をプッシュします。見た目は無害です。ボタンのラベルが変更され、条件付きレンダリングが簡略化され、ヘルパーホックが新しいbranchを取得します。プルリクエストはクリーンで、レビューは速く、デプロイは正常に進みます。

1時間後、サポートから1つのプラットフォームでログインが機能しないという報告が入ります。Webは正常です。デスクトップシェルには古いレンダリングパスがあります。モバイルビルドは非同期状態の変更後異なる動作を示します。誰も気づかなかったのは、codeがテストを実行していたからですが、正しいテストは実行していませんでしたし、テストを取り巻く信頼性のあるシステムもありませんでした。

実際の問題は、Reactの単体テストをプロダクションチームで行うことです。何らかのテストを書くことは簡単です。リファクタリング、リリーストレイン、ホットフィックス、クロスプラットフォームパッケージングを含む、テストがあなたを保護するようにするためのスイートを構築することは難しいことです。Reactアプリケーションは、チームが呼び出す方法を忘れたからではなくて render()テストは実装詳細に傾き、非同期動作は覆い隠され、CIはテストをチェックボックスとして扱うのではなく、リリースゲートとして扱うのを避けます。

現代の単体テストは、Reactが機能するときに、安全システムのように振る舞う必要があります。ローカルで迅速なフィードバック。CIで決定論的なチェック。単体テストと単体テスト以外の境界を明確に定義することが重要です。そうすることが、同じReactコードベースをブラウザ、Capacitorコンテナ、またはElectronシェルを通じて配信することの重要性をさらに高めます。

目次

ユニットテストはReactのベストセーフティネットです

Unit tests earn their keep when they catch the mistake you were confident couldn’t happen. In React, that usually means a component still renders, but the behavior a user depends on has changed. A disabled button becomes clickable. A loading state never clears. A fallback message disappears after a refactor. Those failures are small in code and expensive in production.

React のテスト方法は大きな変化を遂げました。 React Testing Library がメインストリームのテストモデルとなり、内部構造ではなく、ユーザーが見える動作に焦点を当てたテストにチームを導きましたReact Native のテストガイドラインは「 React Native のテスト概要. That shift matters because React code gets rearranged constantly. Hooks move. components split. Context gets introduced. A test tied to internal structure breaks during healthy refactors. A test tied to visible behavior usually survives.

ユニットテストが保護すべきものは何か

良い React のユニットテストは、1 つの小さな契約を保護します。

  • レンダリングされた出力: ユーザーは正しいテキスト、ラベル、状態、またはフォールバックを見るか?
  • インタラクションの動作: クリック、入力、または切り替えが UI を正しく変更するか?
  • 境界処理: コンポーネントが予想される入力、欠落データ、またはエラーパスを受け取ったときに正しく動作するかどうか?

弱いテストは間違ったものを保護します:

  • コンポーネントの内部: 状態の形状、プライベートメソッド、実装専用のプロパティ
  • フレームワークの機械: Reactが内部で予想どおりにハックを更新したかどうか
  • 子要素の詳細: ネストされたコンポーネントがテスト対象外のマークアップ

実用的なルール: ユーザーが見たり行うことができるものを変更せずにコンポーネントをリファクタリングできる場合、テストも変更する必要がない

ユニットテストは、ブラウザレベルテストやデバイスレベル検証パスの必要性を待たずにレグレッションをキャッチするための高速層です。 自動テストのための生産アプリ.

Reactチームが頻繁にリリースする場合、信頼は労働の分割から来ます。単位テストはローカルなバグを迅速に検出します。統合テストはシームズを検証します。エンドツーエンドのテストは重要なパスを確認します。単位層をスキップし、すべてのスローダウンには負担が多すぎます。

モダンなReactテスト環境の設定

脆弱なテスト環境は、単一のアサーションを書く前にフレイキーテストが生じる可能性があります。多くの開発者は、Jest、jsdom、またはReactが原因であると考えますが、実際の問題は、ローカルマシンとCI間で一貫した構成が不一致であることです。対処法は、環境を面白くしないことです。面白くないことはここでは良いことです。

コンピュータモニターがReact単位テストを表示するcodeのcodeエディターを特徴とするきれいなワークスペース。

予測可能なランナーと環境から始めましょう。

モダンなReactアプリ、特にViteで作成されたものの基準設定には、以下が含まれます。

  • テストランナー: Jestは、古いReactコードベースやエンタープライズCIスタックでよく使用されます。
  • ブラウザのような環境: jsdom コンポーネントテストがDOM出力をレンダリングできるようにします。
  • テストライブラリのユーティリティ: @testing-library/react かつ @testing-library/jest-dom
  • A single setup entrypoint: One file to register matchers and global mocks

Reactのテストガイドラインで強調されるのは、シンプルなワークフローです。jsdom-backed環境でコンポーネントをレンダリングし、セレクタでUIを検索し、 getByText or getByRole, インタラクションをトリガーし、DOMの変更を確認するというものです。 Reactテストドキュメント。 そのワークフローは、すべてのマシンが同じテスト環境を実行する場合にのみ信頼できます。

A practical Jest setup通常は次のようになります。

// jest.config.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
  moduleNameMapper: {
    '\\.(css|less|scss)$': 'identity-obj-proxy',
    '^@/(.*)$': '<rootDir>/src/$1',
  },
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
  },
};

あなたのチームがSWCをBabelの代わりに使用している場合、それでも問題ありません。トランスフォーマーではなく、consistencyが重要です。リポジトリで標準化するパスを選択し、もし良いより広いJavaScriptテストの慣習のための補助リファレンスが必要であれば、Capgoの unit tests in JavaScript guide は、チームのハンドオフドキュメントとして役立ちます。

セットアップファイルを追加します。ここで、スイートが依存するファイルを指定します。

適切な setupTests.js 環境のギャップを解決する必要があるため、テストファイルに繰り返し書く必要があるノイズが削減されます。

import '@testing-library/jest-dom';

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

ここで、UIが依存するAPIのモックを追加します。例えば、 matchMedia, ResizeObserver、または IntersectionObserver、もしコンポーネントライブラリがそれらを必要とする場合。

これを実行しないと、開発者はグローバル変数をアドホックで修正します。その結果、不一致のテストとトラッキングが困難なエラーが生じます。1人の開発者がローカルで実行したときにパッチが適用されたのは、手動でモックを追加したファイルが存在したからです。CIで失敗したのは、セットアップが共有されなかったからです。

ローカルとCIの動作を同期する

ローカルコマンドとCIコマンドの動作をできるだけ同期する必要があります。開発者がwatchモードで許容的なデフォルト値で実行している場合、CIが厳格な設定で実行している場合、メインの変更後、驚くようなエラーが発生します。スクリプトを明確に定義する

{
  "scripts": {
    "test": "jest",
    "test:watch": "jest --watch",
    "test:ci": "jest --runInBand --coverage"
  }
}

新しいチームメンバーが同じ基準を取得するのに役立つ短いガイドがあります。

デフォルトの値に関する規律が最も影響力のあるセットアップの選択です。アリースを設定ファイルに追加します。環境モックを1つのセットアップファイルに追加します。UIテスト用に jsdom を使用し、可能な限り軽量な環境でユーティリティ用に環境を使用します。各テストが必要とするカスタム動作が少ないほど、システムの信頼性が高まります。

意味のあるコンポーネントテストの書き方

組織はテストを書く問題がない。6か月後でも意味のあるテストを書く問題がある。

Reactコンポーネントのユニットテストの標準パターンはまだ正しい: コンポーネントをレンダーし、ユーザー中心のセレクターでUIをクエリし、イベントをトリガーし、結果のDOMの変更をアサートする実装詳細のことであるstateやpropsなどのことからテストを守るようにすることを、 Reactテストガイドの説明に従う。

テストのトリックは、パターンを制限することである。

ユーザーがアコーディオンを使用するようにテストする Accordion 基本的なコンポーネントを取る。ボタンとタイトルをレンダーする。パネルコンテンツは非表示の状態で始まる。ボタンをクリックするとコンテンツが表示され、可用性の状態が更新される。

そのような挙動は、有用なテストのためのいくつかの挙動である。

  1. 初期レンダーではタイトルが表示されるがコンテンツは表示されない。
  2. トリガーをクリックすると、コンテンツが表示されます。
  3. 再度クリックすると、折りたたみます。
  4. アクセシビリティ属性は、表示状態を反映しています。

最後の点は、よく省略されます。コンポーネントが使用する場合、または役割に基づく構造、確認してください。 aria-expanded, aria-controlsテストは、実装の詳細ではありません。ユーザー向けの契約の一部です。

最も優れたコンポーネントテストは、受けたいことのないバグレポートのように読まれます。

意図に基づいてクエリを選択します。

React Testing Libraryでは、さまざまなクエリスタイルが提供されますが、互換性はありません。間違ったものを選択すると、テストが雑音や誤解を招くようになります。

クエリタイプ要素が見つかった場合要素が見つからない場合使用例: {__CAPGO_KEEP_0__}
getBy__CAPGO_KEEP_0__を即座に返します__CAPGO_KEEP_0__は即座にエラーを投げます__CAPGO_KEEP_0__はボタンまたはヘッダーがすでに画面に表示されていることを確認します
queryBy__CAPGO_KEEP_0__を即座に返します__CAPGO_KEEP_0__ null__CAPGO_KEEP_0__は非表示のコンテンツがインタラクションの前に存在しないことを確認します
findBy__CAPGO_KEEP_0__が表示されるまで待ちます__CAPGO_KEEP_0__が待機後拒否します__CAPGO_KEEP_0__は、フェッチまたは遅延更新後に非同期ロードされたコンテンツが表示されることを確認します

単純なメンタルモデルが役立ちます:

  • __CAPGO_KEEP_0__ getBy __CAPGO_KEEP_0__はすでに存在するものに使用します
  • 使用 queryBy __CAPGO_KEEP_0__
  • UIの変更が必要な場合に使用 findBy __CAPGO_KEEP_1__

__CAPGO_KEEP_2__で始まるテストは、通常、コンポーネントの更新がいつ発生するかについての不確実性を表しています。その不確実性は、後でフラッキネスになります。 findBy 実際的なアコーディオンの例

ここでは、代表的なコンポーネントを示します。

ここでは、保管価値のあるテストの形を示します。

function Accordion({ title, children }) {
  const [open, setOpen] = React.useState(false);

  return (
    <section>
      <button
        aria-expanded={open}
        aria-controls="accordion-panel"
        onClick={() => setOpen(prev => !prev)}
      >
        {title}
      </button>
      {open ? (
        <div id="accordion-panel">
          {children}
        </div>
      ) : null}
    </section>
  );
}

欠けているものも重要です。内部状態に対するアサーションがない。呼ばれたかどうかのチェックがない。全体のレンダリングされた木のスナップショットがない。そういったテストは、信頼性を高めるのではなく、メンテナンスを増やします。

import { render, screen, fireEvent } from '@testing-library/react';

test('renders the accordion title and hides content initially', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  expect(screen.getByRole('button', { name: /shipping details/i })).toBeInTheDocument();
  expect(screen.queryByText(/delivery takes 3 days/i)).not.toBeInTheDocument();
});

test('reveals content when the trigger is clicked', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  fireEvent.click(screen.getByRole('button', { name: /shipping details/i }));

  expect(screen.getByText(/delivery takes 3 days/i)).toBeInTheDocument();
});

test('updates aria-expanded when opened', () => {
  render(<Accordion title="Shipping details">Delivery takes 3 days</Accordion>);

  const button = screen.getByRole('button', { name: /shipping details/i });
  expect(button).toHaveAttribute('aria-expanded', 'false');

  fireEvent.click(button);

  expect(button).toHaveAttribute('aria-expanded', 'true');
});

コンポーネントテストを強くするには、以下の習慣を身につけることが必要です setOpen __CAPGO_KEEP_0__

__CAPGO_KEEP_1__

  • ロールベースのクエリを優先してください: ボタン、ヘッダー、ダイアログ、警告、入力フィールドは通常ロールで検索できます。
  • 各テストは狭く抑えましょう: 1つのユーザーが見ることができる動作ごとにテストを実行すると、エラーが読みやすくなります。
  • テスト名は結果に基づいて命名しましょう: 「aria-expandedを更新する」は「正しく動作する」というよりもはるかに有用です。

コンポーネントがDOMを通じてテストが難しい場合、それはデザイン上の問題を示唆していることがよくあります。状態を間違った場所に隠している可能性があります。意味のあるマークアップが不足している可能性があります。良いテストはチームをより良いコンポーネントに向かわせることがよくあります。

カスタムフックとアプリケーションロジックのテスト

React apps hide a lot of important behavior outside components. State transitions live in hooks. Validation and formatting live in helper functions. Data shaping often happens before anything renders. If you only test visible components, you’ll miss a large part of the code that can still break production behavior.

カスタムフックにはReactに対応したハーネスが必要です

カスタムフックはまだReactで実行する必要があるため、テストするには renderHook 状態を変更する呼び出しをラップしてください act().

A small useToggle hook は良い例:

import { useState, useCallback } from 'react';

export function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);
  const toggle = useCallback(() => setValue(current => !current), []);
  return { value, toggle };
}

public contract に焦点を当てたテストが必要です:

import { renderHook, act } from '@testing-library/react';
import { useToggle } from './useToggle';

test('returns the initial value', () => {
  const { result } = renderHook(() => useToggle(true));
  expect(result.current.value).toBe(true);
});

test('toggles the value', () => {
  const { result } = renderHook(() => useToggle(false));

  act(() => {
    result.current.toggle();
  });

  expect(result.current.value).toBe(true);
});

hook 自体がユニットであるため、テストはその外部動作を検証することになります。 React の内部動作をテストするのではなく。

再利用可能な UI または機能原子を構築する製品チームにとって、このパターンは非常に重要です。 Hooks はアプリ間、デザインシステム、または内部ツール間で共有されるインターフェイスになります。商用意図で再利用可能な動作を設計する場合、 hooks for makers' products は、hooks を製品化された構築ブロックとしてではなく、実装詳細としてみなすのに役立ちます。

純粋なロジックはテストで純粋なままにすべきです。

すべてのテストに React、または Testing Library が必要ではない場合があります。純粋な関数は、Node 環境で plain Jest でテストすることができます。 jsdom例:

テストは非常に単純でなければなりません:

export function formatDisplayName(firstName: string, lastName: string) {
  return `${firstName.trim()} ${lastName.trim()}`.trim();
}

__CAPGO_KEEP_0__

import { formatDisplayName } from './formatDisplayName';

test('joins and trims both names', () => {
  expect(formatDisplayName(' Ada ', ' Lovelace ')).toBe('Ada Lovelace');
});

test('handles a missing last name', () => {
  expect(formatDisplayName('Ada', '')).toBe('Ada');
});

The win here is speed and clarity. When a function doesn’t need a rendered tree, don’t give it one. React-specific tooling adds overhead. Keep business logic tests small, fast, and close to the function they verify.

A practical split works well:

  • Hooks: Use __CAPGO_KEEP_0__ and __CAPGO_KEEP_1__ when needed. renderHook, act()Utilities:
  • Use plain Jest and no DOM. Stateful cross-cutting logic:
  • Pull it into testable helpers when the component test starts doing too much. Teams often overstuff component tests with logic assertions that belong lower in the stack. Pulling that logic out gives you two benefits. The component test gets cleaner, and the logic test gets faster.

Mastering Advanced Techniques Mocking and Async

Most unreliable React suites break in two places. They break at dependency boundaries, and they break around time.

__CAPGO_KEEP_2__

asyncテストとモッキングは、リリース前に信頼できるテストスイートと玩具テストスイートの境界線です。 一つの分析では、環境またはリソースに関連する問題であるasyncタイミングによるテストの不安定性が46.5%を占めているとされています。 このReactユニットテスト分析によると . Reactアプリでは、直接stateの移行、遅延レンダリング、ネットワークドライブのUI、待ち合わせるのではなく予測するテストにマップします。 Advanced Reactテスト技法の比較表を示すもので、Mocking DependenciesとAsynchronousテストに特に焦点を当てています。境界をモックするのではなく、すべての層をモックすることはありません

テストを誤解を招く最速の方法は、モックしたコンポーネントツリーの半分をモックし、自分のモックが機能したことを確認することです。

アカウントデータを取得するコンポーネントの場合、ネットワーククライアントまたは__CAPGO_KEEP_0__モジュールをモックするのではなく、ホック、子行コンポーネント、ローディングスピナー、3つのユーティリティ関数をモックしないでください。

テストが実際に隔離が必要な部分に限ります。

For a component that fetches account data, mock the network client or API module. Don’t mock the hook, the child row component, the loading spinner, and three utility functions unless the test truly needs isolation at those seams.

外部サービスをモックすることです。

  • HTTPクライアント、分析、ブラウザのみのAPI、ネイティブブリッジ Mocking Dependencies versus Asynchronous Testingの比較表を示すもので、Advanced React Testing Techniquesに特に焦点を当てています。
  • 不安定なプラットフォームAPIをモックします: matchMedia, タイマー、Electronのプリロードインターフェイス、Capacitor プラグインがjsdomで利用できない場合
  • デフォルトでは自分の内部をモックしないように避ける: カスタムフック、シンプルな子孫、ローカルユーティリティ

テストがすべての難しい部分を偽物に置き換えた場合にパスした場合、リリースの信頼性が得られていないことを意味します。

ランナーAPIの例とパターンを提供したいチーム向けのCapgo Jestのカテゴリ Reactを知っているがテストメカニズムを知らない開発者がオンボーディングされている場合、実用的なリファレンスライブラリです。

タイミングが曖昧な場合、非同期テストが失敗します。

非同期の失敗は、次の3つの間違いから生じることが多い:

  1. テストが早すぎます。
  2. テストが任意のタイマーで待機しています。
  3. コンポーネントは1回のトランジションをモデル化するテストのみを更新するが、実際には複数回更新されます。

安定した非同期テストは、この形を持ちます:

test('shows user details after data loads', async () => {
  render(<UserProfile userId="42" />);
  expect(screen.getByText(/loading/i)).toBeInTheDocument();

  expect(await screen.findByText(/account owner/i)).toBeInTheDocument();
});

または、特定の条件を待つ必要がある場合:

await waitFor(() => {
  expect(screen.getByRole('alert')).toBeInTheDocument();
});

使用 findBy 1つの要素の表示がイベントとして重要な場合に使用します。 1つのクエリで状態を表現できない場合、または条件が広い場合に使用します。 waitFor タイマー動作をテストし、偽のタイマーを使用している場合にのみ、テストで使用します。 setTimeout Reactのテストエコシステムでは、更新の意味を尊重することが期待されます。 Testing Libraryは多くのことを処理しますが、状態を手動で操作している場合やタイマーを進めている場合でも、更新がフラッシュするタイミングを考慮する必要があります。

どのモッキングツールを使用するかを知っておく act() モッキングツールは異なる問題を解決します:

ツール

ツール

ツール最良の使用方法よくある間違い
jest.fn()スタンドアロンで偽のコールバックまたはインジェクトされた関数を使用する単純なコールバックが十分であれば、モジュール全体を置き換えるのではなく
jest.spyOn()実際のオブジェクトまたはモジュールの1つのメソッドを観察またはオーバーライドする元の実装を復元することを忘れる
jest.mock()モジュール依存性をインポート境界で置き換えるデフォルトで大規模なモジュールをモックし、意味のある動作を失う

例は以下のとおりです:

  • 必要なときに jest.fn() コンポーネントが onSubmit プロパティを受け取る場合
  • 使用する jest.spyOn() 必要な場合に検証する console.error, 保存方法、または 1 つのエクスポートされた API コール。
  • 使用する jest.mock() モジュールをインポートする場合、I/O、ネイティブ code、またはユニット境界外の動作にヒットしないようにする。

現代の React のエラー パス テストは、多くのガイドが不足している一つの高度な領域です。エラー バウンダリー、遅延状態変更、非同期フォールバック UI は、単に “ハッピーパス” クリック例だけではなく、最初のクラス テストを受けるべきです。子要素が例外を投げた場合、フォールバック UI を確認すること。要求が失敗した場合、表示される回復状態を確認すること。ロード中のボタンが無効になっている場合、も確認すること。ユーザーが思い出すのはそのようなバグです。

テストの品質と戦略の向上

多くのチームは、信頼性とカバレッジが同じものであると考えていますが、実際にはそうではありません。

カバレッジ目標に達することも、重要なレグレッションを逃すこともできます。浅いアサーション、広いスナップショット、内部モックのスイートは、安全性の見せかけを生み出し、メンテナンスコストを高くします。

テスト品質と量のメンテナンスオーバーヘッドの利点を比較するグラフィック。

カバレッジは地図、目標ではありません。

カバレッジ レポートは、重要なパスがまだ保護されていないものかどうかを答える質問に答える場合に役立ちます。

開発者を、単純なラッパー、静的マークアップ、または1行のパススルー ファイルのテストに押し付けることは、有効ではない。カバレッジを発見のためのツールとして扱う。認証状態、請求アクション、機能フラグ、または更新のプロンプトがテストされていない場合、それは信号です。プレゼンテーショナル アイコン コンポーネントがテストされていない場合、通常、それはありません。

健康的なレビューの質問は単純です: このテストはリリースリスクを減らしますか?

  • はい: ユーザーが見える動作を検証するためです。
  • もしかしたら: ビジネスロジックを保護するためです。リファクタリングの際に簡単に壊れやすい部分です。
  • いいえ: 実装の詳細を主張するか、別のテストの値を重複するためです。

ユニットテストしないこと

多くのReactガイドでは、欠陥について十分な時間を費やしていません。そのギャップは重要です。オーバーモッキングと実装詳細のテストは、ユーザー体験がまだ壊れているのに、パスするのに脆弱なスイートを作ります。BrowserStackのガイド "Reactでテストしないこと"に記載されているように 次のパターンをスキップまたは厳格に制限する.

__CAPGO_KEEP_0__

  • 内部状態のアサーション: 直接テストしないでください。パネルが開いたかどうかをテストすることができます。 isOpen フレームワークの動作:
  • Reactが効果を呼び出したかどうかをテストしないでください。効果が変更した結果をテストしてください。 第三者ライブラリの内部:
  • 日付ピッカーまたはルーターと統合するテストを実行してください。ライブラリのレンダリングロジックをテストしないでください。 オーバー・ブレイクされた単位:
  • すべての子やヘルパーをモックした場合、意味のある動作をテストできなくなっている可能性があります。 悪いテストは、リファクタリングをブロックし、実行時エラーを検出できなくても、生産的なバグを検出できなくなるため、欠けているテストよりも悪いです。

__CAPGO_KEEP_0__が所有する境界の所有権という便利なヒューリスティックは、__CAPGO_KEEP_0__が所有するものをテストし、React、ブラウザ、または成熟したライブラリが既に所有しているものはテストしないでください。ただし、統合層が契約を変更する場合に限ります。

A useful heuristic is boundary ownership. Test what your code owns. Don’t test what React, the browser, or a mature library already owns unless your integration layer changes the contract.

スナップショットは、テストが正しく実行されていることを確認するために役立ちますが、テストの複雑さを増やし、テストの結果を理解するのが難しくすることもあります。

Snapshots は無駄ではありません。ただし、簡単に誤用される可能性があります。

UI コンポーネントの安定したシンプルな出力が必要な場合に限り、広範囲にわたる構造的 diff が意味をなす場合に限り、sparingly 使ってください。

UI コンポーネントがインタラクティブまたは高度に動的である場合には避けてください。なぜなら、それらはノイズになり、開発者は読まずに自動的に更新するようになります。

  • より良い代替案が通常存在します:
  • 条件付きレンダリングの場合、key テキストの存在または非存在をアサートしてください。
  • 視覚的な状態の変更の場合、重要な役割、ラベル、または属性をアサートしてください。

エラーとフォールバックの場合、実際のメッセージまたは警告領域をアサートしてください。 あなたのチームがユニットテストのより広範な品質プロセスが必要であれば、 アプリケーション品質保証ワークフロー

テスト、リリースチェック、ロールバック計画を一つのシステムとして扱うことが、テスト品質を最速で改善する意識の変化です。テストの数を尋ねるのではなく、ユーザーに到達する可能性のある失敗について尋ねるようになりましょう。

CI/CD Pipelines にテストを統合する

開発者用ラップトップ上でしか実行されないテストスイートは、提案ではなく制御ではありません。テストスイートは、すべてのプルリクエストが同じチェックをクリーンな環境で実行し、チェックが失敗した場合にマージをブロックするようにすることで、実行可能になります。そうは思っているかもしれませんが、多くのチームは依然として重要な欠陥を残しています。テストは手動で実行されます。カバレージレポートはオプションです。パッケージングとリリースジョブは、テストジョブが完了する前に開始されます。そうでなければ、小さな UI 回帰は大きなリリース失敗に変わります。

5つのステップのフロー図は、CI/CD開発パイプラインにReactの自動テストを統合するプロセスを示しています。

プルリクエストは、毎回同じゲートをトリガーする必要があります。

Reactの単体テストで安全ネットとして機能させるには、CIにはいくつかの基本的なものが必要です:

  • プルリクエストごとに実行する
  • ロックファイルから依存関係をインストールする
  • 毎回同じテストコマンドを使用する
  • テストの失敗で速やかに失敗する
  • テストが成功した後のみアーティファクトを公開する

これは、 アプリチームのための継続的デプロイの実践の核です。リリース前に信頼性を築くのではなく、リリース後に

多くのチームでは、単純なGitHub Actionsワークフローが十分です。

name: test

on:
  pull_request:
  push:
    branches:
      - main

jobs:
  react-tests:
    runs-on: ubuntu-latest

    steps:
      - name: Check out code
        uses: actions/checkout@v4

      - name: Set up Node
        uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm

      - name: Install dependencies
        run: npm ci

      - name: Run unit tests
        run: npm run test:ci

This isn’t fancy, and that’s the point. The strongest pipelines are usually the least surprising ones.

Why this matters more for Capacitor and Electron

Cross-platform React apps carry more release risk than browser-only apps because the same UI code often ships in different containers with different runtime assumptions.

いくつかの例を挙げると、パイプラインがどのように役立つかがわかります。

  • Capacitor apps: Web code may pass locally but fail when a plugin bridge, offline state, or app lifecycle edge case changes behavior after packaging.
  • Electronアプリ レンダラー コンポーネントは、プレロード API、ウィンドウ メッセージング、またはデスクトップのみの状態に依存する可能性がありますが、これらは通常のブラウザテストでは存在せず、意図的にモック化する必要があります。
  • 共有リリーストレイン 1 つの不良のバンドルが複数のターゲットに影響を与える可能性があるため、デプロイメントプロセスが出版を厳密にゲートする必要があります。

したがって、ユニットテストはパッケージングジョブの前に実行し、パッケージングジョブは配布ジョブの前に実行する必要があります。各ステージはリスクを狭めます。ユニットテストはローカルなレグレッションを早期に検出します。プラットフォームパッケージングは環境の仮定を検証します。最終的なリリースの信頼性は、手動の承認またはステージドロールアウトで管理します。

A practical GitHub Actions workflow

より成熟したパイプラインでは、責任を分割することが一般的です:

  1. テストジョブ: 高速なユニットとハックテスト
  2. ビルドジョブ: テストが成功した後のみ、プロダクションビルド
  3. パッケージジョブ: Capacitor 同期、Electron パッケージング、またはアーティファクトバンドリング
  4. リリースジョブ: 承認されたブランチまたはタグからのみ公開

For teams shipping live updates to Capacitor or Electron apps, this is where release tooling matters. One option in that workflow is Capgo, です。これは、ロールバック機能とチャネルベースのロールアウト制御を備えた、CapacitorJSとElectronアプリ用に署名されたWebバンドルを公開するものです。実際には、Reactテストジョブは、Webバンドルがステージングまたはプロダクション配信に昇格される前に最初のハードゲートとして機能することを意味します。

The operational rule is straightforward. Don’t let release infrastructure compensate for weak tests. Use release infrastructure after reliable tests have already filtered out bad changes.

A dependable testing system changes team behavior. Engineers merge with less hesitation. Reviewers focus on edge cases instead of re-running basics manually. Release managers stop treating every deploy like a gamble. That’s the outcome of doing unit testing React well.


If your team ships React through Capacitor or Electron, release safety depends on more than green local tests. Capgo provides teams with a controlled way to publish signed web updates, target rollout channels, and roll back bad bundles without waiting on store review, which fits naturally behind a CI pipeline that already requires passing unit tests before deployment.

リアルタイムの更新機能をCapacitorアプリに搭載

ウェブ層のバグが生じた場合、Capgoを通じて修正を配信し、アプリストアの承認待ちの日数を省略する。ユーザーはバックグラウンドで更新を受け取り、ネイティブの変更は通常のレビュー経路で進む。

Get Started Now

Latest from our Blog

Capgoは、プロフェッショナルなモバイルアプリを作成するために必要な最良の洞察を提供します。