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

フレームワークは必須
最初の間違いは、ユニットテストを副次的な活動として扱うことです。その結果、不一致のファイル名、誰も覚えていないカスタム断言、誰もが理解できないヘルパーが生まれます。
フレームワークは共通の言語を提供します。
- テスト構造 、
describe、test、it - 断言 読みやすいマッチャーとともに
- Hooks セットアップと解放のために
- 非同期サポート プロミスとタイマーのために
- 外部依存性のためのモッキングツール あなたのチームも、ユニットレベルの作業を超えたテスト自動化のより広い視点が必要なら、__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は、異なる哲学を表しています。.
Jest
は、ほとんどのチームが最初の日から必要なものをすべて含むオールインワンオプションです。
Async support for promises and timers Mocking tools for external dependencies
Mocha モックアはよりモジュラーです。ランナーを提供し、残りのスタックを組み立てることを期待します。
| 機能 | Jest | Mocha |
|---|---|---|
| セットアップの複雑さ | ほとんどのチームでは低くなります。 | 高くなるのは、通常アサーションやモッキングライブラリを追加するためです。 |
| アサーション | 組み込まれている | 通常は別のライブラリと組み合わせて使用します。 |
| モッキング | 内蔵 | __CAPGO_KEEP_0__と組み合わせることが多い |
| 非同期テスト | 内蔵で簡単 | __CAPGO_KEEP_0__はサポートされているが、周辺設定に依存する |
| カバレッジワークフロー | __CAPGO_KEEP_0__と組み合わせることが多い | __CAPGO_KEEP_0__は別々に組み立てられることが多い |
| 最適な選択 | 新規プロジェクト、統一性を求めるチーム | 既存のスタック、モジュラー制御を求めるチーム |
実用的なルール: あなたのチームがランナーよりもアサーションライブラリとモッキングライブラリをペアする必要がある場合、Jestを使用したいと思います。
大多数のチーム向けの推奨事項
大多数の現代的なプロジェクト向けの推奨事項 Jest Mochaが既存のコードベースで強い理由がある場合を除き、Mochaを使用することをお勧めします。 Capacitor または Electron、は既存のエコシステムが十分に安定している古いNode.jsサービスまたは長期間にわたるコードベースではMochaがまだ意味をなします。
しかし、Mid-levelエンジニアがスクラッチから強力なセットを設定する場合、Jestは通常、より多くの摩擦を生み出さず、テストツールのスプレッドを削減することで早く利益を得ることができます。
重要な範囲の注記。CypressとPlaywrightは優れたツールですが、異なる問題を解決します。
ブラウザレベルとエンドツーヨンチェックのために、JavaScriptのユニットテストの高速な内部ループでは、CypressとPlaywrightは効果的ではありません。
__CAPGO_KEEP_0__

__CAPGO_KEEP_0__
__CAPGO_KEEP_0__ package.json__CAPGO_KEEP_1__
{
"scripts": {
"test": "jest"
}
}
__CAPGO_KEEP_0__
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_0__
Write the test before the code
__CAPGO_KEEP_0__ __CAPGO_KEEP_0__テストの組織と describe そして it、そして expect(...) の確認をテストの のJavaScriptのユニットテストのガイドライン.
それが重要なのは、テストから始めることで設計が変わるからです。codeの関数は小さくなり、依存関係は明らかになり、副作用は純粋でなければならないロジックに漏れ込むのを止めるのです。
ここに最小限の例があります。
// 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);
});
});
Arrange Act Assertを毎回使ってください。
Arrange, Act, Assert パターンは、テストが複雑になるにつれても、読みやすく保つことができます。 Arrange
- Arrange, Act, Assert targetLanguage":"Japanese","protectedTokens":["Cloudflare","Capacitor","GitHub","Capgo","code","API","SDK","CLI","npm","bun"],"texts":["入力値と必要なセットアップを含めてください。",
- Act ,
- by calling the function. on the outcome.
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);
});
});
Small tests age well. A test should usually answer one question, not narrate an entire workflow.
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.
Mastering Mocks and Asynchronous Code
Most bugs in application code don’t come from adding two numbers. They come from code that reaches outside itself: network requests, files, plugin APIs, timers, IPC channels, storage layers.
That’s where mocking helps. It gives you control over the boundary so the test can focus on your code’s decision-making.

モックの境界を設定せずにすべて
テストの可維持性を重視するガイドラインは 単一の動作カバレッジ そして テストごとに1つの強力なアサーション, そしてまた、モックを過度に使用すると、テストが実装の詳細に密接に結びつき、脆弱になることも警告しています。これは、 TestRailの「可維持性の単体テスト」に関する記事でまとめられています。.
JavaScriptでは、この警告は非常に重要です。チームは、すべてのインポートされたモジュールをモックすることから始め、実際の動作をテストするのではなく、関数が他の関数を「正しく」呼び出すかどうかをテストするのではなく、
モックが多すぎるテストの悪い目標:
- ヘルパーAがヘルパーBを呼び出したかどうか
- サービスCがシリアライザーDを呼び出したかどうか
- 内部プライベート関数が2回実行されたかどうか
より良い対象:
- 関数が返した値
- 依存関係の失敗を正しく処理したかどうか
- データを期待どおりの形に変換したかどうか
CapacitorとElectroncodeのためのより良いパターン
モバイルとデスクトップアプリでは、ネイティブまたはプラットフォーム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でもそのパターンが機能します。 ipcRendererWrap
For teams testing release logic and update paths in Capacitor apps, Capgo has a relevant guide on Capacitorアプリのリリースロジックとアップデートパスをテストするチーム向けに、__CAPGO_KEEP_1__は.
__CAPGO_KEEP_0__のOTAアップデートをモックシナリオでテストするためのガイドを提供しています。
非同期フローを不安定性なくテストする
テストで 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');
});
両方のパスをテストする。実用的なテストスイートは、__CAPGO_KEEP_0__が変更された後でも有用なままになることが重要です。
強力なテストのための高度な戦略
テストスイートは、codeが変更された後でも有用なままになることが重要です。単にパスするテストを大量に書くことよりも難しいです。

__CAPGO_KEEP_0__の変更に対してテストスイートのコストを分割する
実用的なガイドでは、 70/20/10 __CAPGO_KEEP_0__の変更に対してテストスイートのコストを 単体テスト、統合テスト、エンドツーエンドテストに分割する、ユニットテストで最速のフィードバックと最安定なエラーを提供するように指示されています。同様のガイドでは、ユニットスイートが理想的には10秒以内に完了し、pre-commitチェックは5秒以内に完了するようにすることを推奨しています。このOpenReplayテストガイドを参照してください。 私にとっては、バジェットツールではなく、宗教ではありません。エンドツーエンドテストに多くの努力を費やした場合、チームはフィードバックを待つことになるでしょう。ユニットテストのみに焦点を当てた場合、システムの境界を無視することになるでしょう。CapgoまたはElectronアプリの場合、健康的なバランスは次のようになります。 ユニットテスト価格ロジック、パーミッションルール、シリアライズ、更新の有効性、機能フラグ、状態変換のテスト 統合テスト.
ストレージアダプター、プラグインラッパー、IPCコントラクトのテスト
Capacitor
- __CAPGO_KEEP_0__ __CAPGO_KEEP_0__
- __CAPGO_KEEP_0__ __CAPGO_KEEP_0__
- E2Eテスト ログイン、購入フロー、同期、または更新の促進などの重要な数少ない旅程に対して
カバレッジはフラッシュライト、ターゲットではない
カバレッジレポートは、重要なロジックの未テストのbranchを発見するのに役立つ場合に有用ですが、チームがカバレッジのパーセンテージを追求するためにそれ自体で害を及ぼす場合には有害です。
ログイン検証器に思いやりのあるエッジケーステストを実行すると、重要なUIの検証が重視されているチームにとって、重要なロジックのカバレッジが高いファイルに多くのトリビアルアサーションを含むことよりも価値があります。特に、フォーム、パーサー、日付ロジック、許可チェックなどの入力が多いcodeに対してです。チームが検証が重視されるUIの品質を高めている場合、この フロントエンドフォーム検証のマスター のガイドは、ユニットレベルのテスト戦略の補完として役立ちます。
振る舞いから始まるテストはリファクタリングを乗り越える
信頼できるスイートは、内部のリファクタリングを行うとテストの半分を書き直さなければならないことなく、リファクタリングを乗り越えることができます。実装詳細をアサートするのではなく、観察可能な振る舞いをアサートすることによって、そこにたどり着くのが一番の方法です。 ユースケースがよく通用するもの: ログイン
購入フロー
- 境界条件 __CAPGO_KEEP_0__
- ドメイン結果 例:「許可が欠如しているため、戻り値が拒否される」
- 状態遷移 例:「ダウンロードメタデータが検証された後、更新を保留状態としてマークする」
よく使われていない用途
- 内部ヘルパー呼び出しを検査する
- プライベートメソッドのシーケンスを確認する
- 呼び出しチェーンのすべての層をモックする
アプリチームが厳格なリリースプロセスを構築している場合、Capgoの記事「 アプリケーション品質保証 CIは、テスト作業をより広いリリースパイプラインに接続するため、役立ちます。
CI、Capacitor,およびElectronアプリのテスト
1人の開発者のマシン上で実行されるテストだけでは、安全ネットではありません。ローカルな習慣です。
CIは、JavaScriptの単体テストをチームインフラに変換します。プッシュ、プルリクエスト、またはリリースブランチごとに、同じコマンドと同じ期待値でテストを実行できます。環境の変化が微妙なエラーを引き起こすElectronおよびCapacitorプロジェクトでは、コスクリストの重要性がさらに高まります。
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をテストする間違った方法は、プラットフォームブリッジに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 });
});
カメラアクセス、バイオメトリックのプロンプト、プッシュトークンの登録、ネットワークのステータスについても同じ考え方が当てはまります。アダプター内にプラグインの呼び出しを保ちましょう。アプリのロジックを、自分が制御するインターフェイスでテストしてください。
ElectronのメインレンダラーとIPCをテストするcode
Electronアプリには2つの重要なシームがあります: メインプロセスcode と レンダラー プロセスcode。テストでは、ぼかさないでください。
信頼できるセットアップは、通常、以下を分離します:
- ビュー モデルのユニット テスト ビュー モデル、状態、フォーマット、UI側のビジネス ロジック
- メイン プロセスのユニット テスト メニュー、ファイル操作、そしてアプリライフサイクル決定のために
- IPC契約テスト メッセージの形状と期待される応答のために
例の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.
JavaScript単体テストについてよくある質問
単体統合テストとE2Eテストの違いは何ですか
A 単体テスト 1つの小さなロジックの単体テストです。 統合テスト 複数のコンポーネントやサービスが正しく機能するかどうかを確認するテストです。 end-to-end test 実行中のアプリケーションを通じてユーザージャーニーを実行することで機能をテストする。
単体テストを使用してビジネスルールに対する迅速な信頼を確立する。統合テストを使用して、ストレージ、プラグインラッパー、IPCなどのシームをテストする。E2Eテストは、破壊される可能性のあるワークフローをテストするためにsparingly使用する。
完全なカバレージを目指すべきか
いいえ。完全なカバレージは、チームを低価値のテストに導く可能性がある。
カバレージは、誰も実行していないリスクのあるcodeを明らかにするのに役立つ。エンジニアがダッシュボードを満たすために浅いアサーションを追加するのを止めるのには役立たない。スイートが脆い場合、カバレージがそれを救うことはない。
既存のコードベースにテストを追加するにはどうすればよい
変更がすでに発生している場所から始める。チームを凍らせて、テスト戦略の大規模なリライトを発表するのを避ける。
実践的なシーケンスは次のようになる。
- アクティブなcodeを保護する 機能開発またはバグ修正の際に変更するモジュールにテストを追加する
- 純粋なロジックを抽出する 難テストファイルからビジネスルールをテストできるようにするため、フレームワークや実行環境のノイズを排除
- セームラッパーを追加 ネイティブプラグイン、ネットワーククライアント、ファイルシステム呼び出し、Electron IPC周りに
- 脆弱なパターンを拒否 モックを導入する際に JavaScriptテストベストプラクティス 特に、オーバーモッキングとその後に続く脆弱なテストをよく見落とす問題を強調することで、ここでは特別に役立ちます
目標は即時完璧ではなく、チームにとって最もコストがかかる場所での改善の連続
チームが Capacitor または Electron __CAPGO_KEEP_0__ Capgo __CAPGO_KEEP_0__