あなたは現在、2つの状況のいずれかにいるかもしれません。JavaScriptプロジェクトがほとんどテストがなく、リファクタリングがリスキーな感じがする、または既存のテストが半分以上が遅い、脆弱で、信頼できないように感じるものです。
それが Capacitor と Electron アプリケーション。単純な機能は、共有ビジネスロジック、ブラウザAPI、ネイティブプラグイン、ローカルファイル、IPC、リモートサービスと同じフローで触れます。間違った方法でテストすると、スイートは偽の依存関係の迷路になります。正しい方法でテストすると、論理が壊れるときに迅速なフィードバックが得られます。
良いユニットテストは、賢いマッチャーシntaxで始まるのではなく、規則正しい境界から始まります。純粋な論理を直接テストし、副作用を分離し、内部関数をリネームするとテストが崩れるのを避けます。
目次
- JavaScript テストフレームワークを選ぶ
- プロジェクトの設定と最初のテスト
- 非同期Codeとマスターする
- 堅牢なテストのための高度な戦略
- CI、Capacitor、およびElectronアプリのテスト
- JavaScript単位テストに関するよくある質問
JavaScriptテストフレームワークの選択
プロフェッショナルなJavaScriptプロジェクトには、実際のテストランナーが必要です。アドホックのスクリプトとマニュアルのコンソールチェックは、複数のエンジニアが同じコードベースに触れると、スケールしなくなります。テストの発見、断言、非同期処理、モック、CIとローカル開発ですべてを一貫して実行する方法が必要です。
現在のガイドラインは、主流のオプションに集約されています。 Jest、Mocha、Jasmine は、主なフレームワークとして繰り返し強調されています。 Jest 単一パッケージ内でテスト構造、断言、モッキング、非同期サポートを組み合わせたものとしてよく単に単一テストとして挙げられる PluralsightのJavaScriptテストラボ.

フレームワークは必須
チームが最初に犯す間違いは、単一テストを副次的な活動として扱うことです。その結果、不一致のファイル名、誰も覚えていないカスタム断言、しかも誰もが理解できないヘルパーが生まれます。
フレームワークは共通言語を提供します:
- テスト構造 と
describeまたはtest断言it - 可読性の高いマッチャー with
- Hook セットアップと破棄のための
- 非同期サポート プロミスとタイマーのための
- 外部依存性のためのモックツール あなたのチームも、単位レベルの作業を超えたテスト自動化のより広い視点が必要な場合、__CAPGO_KEEP_0__ はアプリケーションの配信ワークフローにおける自動テストの概要を提供しています。
If your team also needs a broader view of test automation beyond unit-level work, Capgo has a useful overview of JestとMochaは、2つの異なる哲学を表しています。.
Jest
すべての機能が含まれたオールインワンオプションです。
day one ワンクリックで
Mocha はモジュラーです。ランナーを提供し、残りのスタックを組み立てるように期待します。
| 機能 | Jest | Mocha |
|---|---|---|
| セットアップの複雑さ | ほとんどのチームでは低い | 通常、断言やモッキングライブラリを追加するため高くなります。 |
| 断言 | 組み込まれている | 通常、別のライブラリと組み合わせて使用します。 |
| モッキング | 組み込み | 通常は別のライブラリと組み合わせて使用 |
| 非同期テスト | 組み込みで簡単 | サポートされているが、周囲の設定に依存する |
| カバレッジワークフロー | 通常は同じツールチェーンに統合される | しばしば別々に組み立てられる |
| ベストフィット | 新しいプロジェクト、統一性を求めるチーム | 古いスタック、モジュラー制御を求めるチーム |
実用的なルール: Jestを使用したい場合は、チームがランナーとアサーションライブラリ、モッキングライブラリを組み合わせる必要があるかどうかを尋ねる必要がある場合、確かにJestを使用したいと思います。
私が大多数のチームに推奨するもの
大多数の現代的なプロジェクトでは、Jestを選択することをお勧めします。 Jest Mochaを使用する理由がすでに固まっているコードベースがある場合は除きます。Capacitorアプリケーションが含まれている場合、Capacitorライブアップデートの代替品を検討する際に、推奨度が強くなります。 Capacitor Electron Electron、これらのプロジェクトはすでに十分な複雑さがあります。テストツールのスプレッドを削減すると、すぐに効果が現れます。
Mochaは、既存のエコシステムがすでに安定している古いNode.jsサービスまたは長期間のコードベースでまだ意味があります。ただし、mid-levelエンジニアが最初からから強力なスイートを設定する場合、Jestはより多くのフリクションを削減します。
一方の重要な範囲の注釈。CypressとPlaywrightは優れたツールですが、異なる問題を解決します。ブラウザレベルとエンドツーエンドのチェックに適していますが、ユニットテストのJavaScriptの内部ループではありません。
プロジェクトのセットアップと最初のテスト
A clean testing setup should be dull. If adding the first test feels complicated, the suite probably won’t stay healthy.

A simple Jest setup
まず、すでにJavaScriptプロジェクトを持っている場合から始めましょう。 package.json.
{
"scripts": {
"test": "jest"
}
}
次に、Jestを開発依存モジュールとして追加し、テストスクリプトを設定します。
If you’re building a Capacitor app locally and want your dev environment in order before adding tests around shared logic, Capgo’s guide to ローカルでCapacitorアプリを構築中で、共有ロジックの周りでテストを追加する前に開発環境を整理したい場合は、__CAPGO_KEEP_1__の __CAPGO_KEEP_0__ローカル環境の設定方法のガイド
Write the test before the code
テストを書く前に__CAPGO_KEEP_0__を実装する。 テストを書く前に実装するというパターンは、単に個人的な好みではありません。米国消費者金融保護局のJavaScriptガイドでは、テストを最初に書くことを明示的に推奨しています。、テストを整理する describe と it, そしてテストのチェックを囲む expect(...) の の.
That matters because test-first changes how you design code. Functions tend to become smaller, dependencies become more visible, and side effects stop leaking into logic that should stay pure.
JavaScriptのユニットテストのガイドライン
// math.js
function addTax(amount, rate) {
return amount + amount * rate;
}
module.exports = { addTax };
// math.test.js
const { addTax } = require('./math');
describe('addTax', () => {
it('returns the amount with the tax applied', () => {
expect(addTax(100, 0.2)).toBe(120);
});
});
それは重要なことです。テストから始めることは、設計方法を変えるためです。__CAPGO_KEEP_0__の関数は小さくなり、依存関係は明らかになり、副作用は論理が純粋でなければならないものに漏れ出なくなります。
ここに最小限の例があります。 Arrange Act Assertを毎回使用する の
- Arrange, Act, Assertのパターンは、テストが複雑になるにつれても読みやすくします。 入力値と必要なセットアップ。
- アクション 関数を呼び出す
- 確認 結果
検証ヘルパーに適用される
function isSupportedPlatform(platform) {
return ['ios', 'android', 'web', 'desktop'].includes(platform);
}
describe('isSupportedPlatform', () => {
it('returns true for ios', () => {
// Arrange
const platform = 'ios';
// Act
const result = isSupportedPlatform(platform);
// Assert
expect(result).toBe(true);
});
});
小さなテストは長く生きる。テストは通常、1 つの質問に答えるようにするべきであり、全体のワークフローを語るべきではない。
For Capacitor and Electron projects, that discipline matters more because your pure logic often sits next to native or desktop integration code. Keep the business rule testable without the platform runtime, and your first test won’t be your last useful one.
モックと非同期Codeのマスター
アプリケーションcodeの多くのバグは、2 つの数字を加算することから来ません。外部に影響を与えるcodeが原因です: ネットワークリクエスト、ファイル、プラグインAPI、タイマー、IPCチャネル、ストレージレイヤー。
モッキングはそこで役立ちます。境界を制御するので、テストはcodeの決定を焦点に置くことができます。

境界を模擬せず、すべて
保守可能なテストガイドは 単一の行動範囲のカバレッジを強調しています そして 1 つの強力なアサーションごとにテスト, そしてまた、過度にモックを使用すると、テストが実装の詳細に依存し、脆弱になり、実際の動作をテストするのではなく、関数が他の関数を呼び出す順序が「正しい」かどうかをテストするのではなく、チームがモックをすべてのインポートされたモジュールで始めて、最終的に実際の動作をテストするのではなく、関数が他の関数を呼び出す順序が「正しい」かどうかをテストするのではなく、 モックが多すぎるテストの悪い目標:.
ヘルパーAがヘルパーBを呼び出したかどうか
サービスCがシリアライザーDを呼び出したかどうか
- 内部プライベート関数が2回実行されたかどうか
- TestRailの記事「保守可能なユニットテスト」
- モックが多すぎるテストの警告は、JavaScriptで大事です。チームは、モックをすべてのインポートされたモジュールで始めて、関数が他の関数を呼び出す順序が「正しい」かどうかをテストするのではなく、実際の動作をテストするのではなく、関数が他の関数を呼び出す順序が「正しい」かどうかをテストするのではなく、
より良い対象:
- 関数が返した値
- 依存関係が失敗した場合に正しく処理したかどうか
- データを期待どおりに変形したかどうか
CapacitorとElectron codeのためのより良いパターン
モバイルとデスクトップアプリでは、ネイティブまたはプラットフォームAPIのラッパー層を好みます。ラッパーをモックすることで、プラットフォーム自体をテストするのではなく、ラッパー層をテストします。
例の構造:
// cameraGateway.js
async function getPhoto(cameraPlugin) {
return cameraPlugin.getPhoto();
}
module.exports = { getPhoto };
// profilePhotoService.js
async function loadProfilePhoto(cameraGateway) {
const photo = await cameraGateway.getPhoto();
return { path: photo.path, ready: true };
}
module.exports = { loadProfilePhoto };
// profilePhotoService.test.js
const { loadProfilePhoto } = require('./profilePhotoService');
test('returns mapped photo data', async () => {
const fakeCameraGateway = {
getPhoto: jest.fn().mockResolvedValue({ path: '/tmp/pic.jpg' })
};
const result = await loadProfilePhoto(fakeCameraGateway);
expect(result).toEqual({ path: '/tmp/pic.jpg', ready: true });
});
Electronでもそのパターンが機能します。ネイティブAPI、ファイルアクセス、またはシェル統合を薄いアダプタの後ろにラップします。サービス層にアクセスすることで、実行時ではなく、ユニットテストを実行します。 ipcRenderer__CAPGO_KEEP_0__アプリのリリースロジックとアップデートパスをテストするチームには、__CAPGO_KEEP_1__が関連するガイドを提供しています。
For teams testing release logic and update paths in Capacitor apps, Capgo has a relevant guide on testing Capacitor OTA updates with mock scenarios.
アシンクロニズドテストスタイルを正常化するプロセスがまだ続いているチームの場合、簡単なウォークスルーが役立ちます。
非同期フローを不安定性なくテストする
使用 async/await codeがプロミスを返す場合、テストで使用します。コールバックが多くあるパターンよりも明確で、デバッグも容易です。
async function fetchProfile(api) {
const response = await api.getUser();
return response.name;
}
test('returns the user name from the API response', async () => {
const api = {
getUser: jest.fn().mockResolvedValue({ name: 'Ava' })
};
const result = await fetchProfile(api);
expect(result).toBe('Ava');
});
失敗パスもテストしてください:
test('throws when the API request fails', async () => {
const api = {
getUser: jest.fn().mockRejectedValue(new Error('network failed'))
};
await expect(fetchProfile(api)).rejects.toThrow('network failed');
});
両方のパスをテストしてください。生産環境では、失敗パスはユーザーが思い出すパスです。
堅牢なテストの高度な戦略
テストスイートは、codeが変更された後でも有用なままになることが重要です。単にパスするテストを大量に書くことよりも難しいです。

テスト分割を予算として使用
1つの実用的なガイドでは、 70/20/10 単位、統合、エンドツーエンドのテストの 割合、ユニットテストは、最速のフィードバックと最も安定したエラーを提供します。同様のガイドは、完全なユニットスイートが理想的には10秒以内に完了するべきであると述べています。 10秒以内、そしてpre-commitチェックは5秒以内に完了するべきです。 5秒以内この OpenReplayテストガイド.
私はそれを予算ツールとして扱いますが、宗教ではありません。エンドツーエンドテストに多くの努力を費やすと、チームはフィードバックを待つことになります。ユニットテストのみがすべての場合、システムの境界を無視することになります。
For a Capacitor or Electron app, a healthy balance usually looks like this:
- ユニットテスト 価格ロジック、パーミッションルール、シリアライズ、更新の有効性、機能フラグ、状態変換のテスト
- 統合テスト ストレージアダプター、プラグインラッパー、IPCコントラクトのテスト
- UIテスト 重要なフロー、ログイン、購入フロー、同期、または更新の促進のための数少ないクライアントサイドテスト
カバレッジは目標ではなく、照明
カバレッジレポートは、重要なロジックの未テストのbranchを発見するのに役立ちますが、チームがカバレッジパーセンテージを追求することで害を及ぼします。
慎重なエッジケーステストを含むログイン検証は、重要なロジックの未テストのbranchを発見するのに役立つカバレッジレポートよりも価値があります。特に、フォーム、パーサー、日付ロジック、パーミッションチェックなどの入力が多いcodeの場合です。UIの検証が重視されるチームが、バリデーションを重視するUIの品質を高めたい場合は、この フロントエンドフォーム検証のマスター のガイドは、ユニットレベルのテスト戦略の補完となるものです。
振る舞いを中心としたテストはリファクタリングに耐える
信頼できるテストスイートは、内部のリファクタリングを行うと、半分のテストを書き直すことなくテストを実行できるようにすることができます。実装詳細をアサートするのではなく、観察可能な振る舞いをアサートすることで、そこにたどり着くのが一番簡単です。 健全なユースケース: refactor
observable
- 境界条件 例えば、空の入力、nullのような値、無効な型、オーバーサイズの文字列
- ドメインの結果 例えば、「許可が欠如しているため返却が拒否される」
- 状態の移行 例えば、「ダウンロードメタデータが検証された後、更新を保留状態としてマークする」
よく使わないケース:
- 内部ヘルパー関数の内部検査
- プライベートメソッドのシーケンスを確認する
- 呼び出しチェーンのすべての層をモックする
厳格なリリースプロセスを構築しているアプリチーム向けに、Capgoの記事「 アプリの品質保証 は、テスト作業をより広いリリースパイプラインに接続することで、有用です。
CI、Capacitor、およびElectronアプリ用のテスト
1人の開発者のマシン上で実行されるテストだけでは、安全ネットではありません。ローカルな習慣です。
CIは、JavaScriptのユニットテストをチームインフラに変換します。プッシュ、プルリクエスト、またはリリースブランチごとに、同じコマンドと同じ期待値でテストを実行できます。この一貫性は、環境の変化が微妙なエラーを引き起こすCapacitorおよびElectronプロジェクトにとって、もっとも重要です。
CIをデフォルトの実行パスに設定する
CIには、依存関係をインストールし、ユニットスイートを各変更セットごとに実行することが必要です。可能な限りローカル開発と同じコマンドを使用してください。
GitHubアクションワークフローを設定するには、以下の最小限の設定が必要です。
name: test
on: [push, pull_request]
jobs:
unit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm test
これだけでも、破損したインポート、失敗したアサーション、意図しないプラットフォームの仮定をメインに到達する前にキャッチできます。
自動化されたパイプラインを通じてモバイルチームが配信する場合、CapgoはCapgoアプリ用のCI/CDの設定方法の実用ガイドを提供しています。 Capacitorプラグインの相互作用をテストする.
ユニットテストでCapacitorを__CAPGO_KEEP_1__するのは間違った方法です。プラットフォームブリッジに直接nativeプラグインをすべてのサービスにpullするのです。その結果、テストスイートがプラットフォームブリッジに結びつきます。
The wrong way to unit test Capacitor code is to pull native plugins directly into every service. That couples your test suite to the platform bridge.
より良いパターンは、薄い抽象化です:
// deviceStorage.js
async function saveFile(filesystem, path, data) {
return filesystem.writeFile({ path, data });
}
module.exports = { saveFile };
// draftService.js
async function persistDraft(storage, draft) {
await storage.save('draft.json', JSON.stringify(draft));
return { saved: true };
}
module.exports = { persistDraft };
// draftService.test.js
const { persistDraft } = require('./draftService');
test('persists a serialized draft', async () => {
const storage = {
save: jest.fn().mockResolvedValue(undefined)
};
const result = await persistDraft(storage, { title: 'Hello' });
expect(result).toEqual({ saved: true });
});
カメラアクセス、バイオメトリックのプロンプト、プッシュトークンの登録、ネットワークのステータスにも同じ考え方が当てはまります。アダプター内でプラグインの呼び出しを維持し、コントロールできるインターフェイスでアプリロジックをテストしてください。
Testing Electron main renderer and IPC code
Electronアプリには2つの重要なシームがあります: main process code そして renderer process code. テストではこれらをぼかさないでください。
信頼できるセットアップでは通常、以下を分離します:
- ビュー モデル、状態、フォーマット、UI側のビジネスロジックのためのレンダラー単位テスト メインプロセス単位テスト
- __CAPGO_KEEP_0__ JavaScriptのユニットテスト
- メニュー、ファイル操作、またはアプリケーションライフサイクル決定のための IPC契約テスト
メッセージの形状と予想される応答のための
// ipcGateway.js
function sendSettings(ipcRenderer, payload) {
ipcRenderer.send('settings:update', payload);
}
module.exports = { sendSettings };
// ipcGateway.test.js
const { sendSettings } = require('./ipcGateway');
test('sends settings update over ipc', () => {
const ipcRenderer = { send: jest.fn() };
sendSettings(ipcRenderer, { theme: 'dark' });
expect(ipcRenderer.send).toHaveBeenCalledWith('settings:update', { theme: 'dark' });
});
If you later change the internal implementation from one helper to another, this test still holds because it verifies the behavior that matters. That’s the standard you want across desktop and mobile code.
あなたが後で内部実装を1つのヘルパーから別のヘルパーに変更した場合、このテストはまだ有効です。なぜなら、それは重要な動作を検証するからです。そうでなければ、デスクトップとモバイルの両方で標準を維持したいと思います。
JavaScriptのユニットテストに関するよくある質問
ユニット統合とE2Eテストの違いは何ですか A ユニットテスト 1つの小さな論理部分を孤立してチェックします。 統合テストは、複数のコンポーネントまたはサービスが正しく機能するかどうかをチェックします。 エンドツーエンドテスト 実行中のアプリケーションを通じてユーザージャーニーを実行する。
ビジネスルールの迅速な信頼性のためにユニットテストを使用します。ストレージ、プラグインラッパー、IPCなどのシームのために統合テストを使用します。ワークフローが壊れた場合に深刻な影響を与えるものは、E2Eテストを控えめに使用してください。
完全なカバレージを目指すべきか
いいえ。完全なカバレージはチームを低価値のテストに導く可能性があります。
カバレージは、誰も実行していないリスクのあるcodeを明らかにするときに役立ちます。ダッシュボードを満足させるために浅いアサーションを追加するエンジニアがいる場合、役に立ちません。スイートが脆弱な場合、カバレージを増やすことでそれを救うことはできません。
既存のコードベースにテストを追加するには
変更がすでに発生している場所から始めます。テスト戦略の大規模なリライトを発表し、チームを凍結する必要はありません。
実用的なシーケンスは次のようになります。
- アクティブなcodeを保護することから始めます。 機能開発やバグ修正の際に変更するモジュールにテストを追加することから始めます。
- 純粋なロジックを抽出します テストが難しいファイルからビジネスルールをテストできるようにするため、フレームワークやランタイムのノイズを排除する
- セームWrappersを追加する ネイティブプラグイン、ネットワーククライアント、ファイルシステム呼び出し、Electron IPC周りの
- 脆弱なパターンを拒否する モックを導入する際に生じる脆弱なテスト JavaScriptテストのベストプラクティスから得られる指導は、オーバーモッキングとその後に続く脆弱なテストをよく見落とす問題を強調している 目標は即時的な完全性ではなく、チームにとって最もコストがかかる場所でのステディイーモプロビメント
チームが
__CAPGO_KEEP_0__ Capacitor Capacitorライブアップデートの代替 Capacitorライブアップデートの代替、またはAppflowの代替 アプリとJavaScriptの変更に対するよりきれいなリリースプロセスが必要です Capgo CapacitorJSとElectronアプリに対するリアルタイムの更新を提供し、ロールアウトの制御と可視性を備えたCapacitorJSアプリの安全なパスを提供します。