Tauriアプリのテスト
Tauri v2 デスクトップアプリケーション固有のテストパターン -- WebKit ファースト(ローカル)+ Chromium-CI のデュアル戦略、コアクレートパターン、バックエンドブリッジのモック、そして完全な8ステップのエスカレーションラダー。
WebKitファースト: 本番エンジンに合わせる
Tauriのレンダリングエンジンはプラットフォームによって異なり、どこでも同じエンジンというわけではありません:macOSではWKWebView、LinuxではWebKitGTK、Windowsでは(Chromiumベースの)WebView2です。TauriフロントエンドのPlaywright E2Eテストを書く際は、macOS/Linuxの本番エンジンに合わせてWebKitを含め、さらにChromiumも含めてください——ChromiumはWindowsのエンジンそのものと一致し、ヘッドレスCIでもより安定して動作します:
// playwright.config.ts
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
projects: [
{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
],
});Warning
Chromiumでだけテストすると、macOS・Linuxユーザーに対して誤った安心感を与えます——これらのプラットフォームでは本番アプリがWebKit(WKWebView / WebKitGTK)でレンダリングされるため、Chromiumのみのカバレッジは本物のWebKit固有のバグを見逃す可能性があります。
推奨されるデュアル戦略:
ローカル検証にはWebKit — macOSとLinuxの本番エンジンに一致します。WebKit固有の挙動(CSSの癖、フォーカス処理、アプリのショートカットエンジン経由のキーボードショートカットなど)を検証するテストにはこちらを使ってください。
CIにはChromium — ヘッドレスCI環境でより信頼性が高く、Windowsユーザーにとってはトレードオフですらありません。TauriのWindows向けWebView(WebView2)はChromiumベースなので、CIでのChromium実行はWindowsに対する本物のエンジンカバレッジをそのまま提供します。
肝心なルール:macOS/Linuxのカバレッジのためにローカルでは常にWebKitで検証し、Playwrightのプロジェクトマトリクスには両方を残してください。CIでDOM限定のテストにChromiumを使うのは、macOS/Linuxにとっては現実的な妥協であり、Windowsにとっては正真正銘のカバレッジです——どちらにしても手抜きではありません。
コアクレートパターン
プラットフォーム非依存のビジネスロジックを、Tauri依存のない別のRustクレートに抽出します。このクレートはTauriアプリケーションコンテキストなしで標準的な cargo test でテストできます:
src-tauri/
Cargo.toml # depends on core + tauri
src/
main.rs # Tauri setup, commands
commands.rs # #[tauri::command] handlers
core/
Cargo.toml # no Tauri dependency
src/
lib.rs
settings.rs # pure Rust logic
file_ops.rs # file operations
transforms.rs # data transforms # core/Cargo.toml
[package]
name = "myapp-core"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"
# No tauri dependency here// core/src/settings.rs
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Settings {
pub theme: String,
pub font_size: u32,
}
impl Settings {
pub fn with_theme(mut self, theme: &str) -> Self {
self.theme = theme.to_string();
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_with_theme() {
let settings = Settings {
theme: "light".to_string(),
font_size: 14,
};
let updated = settings.with_theme("dark");
assert_eq!(updated.theme, "dark");
assert_eq!(updated.font_size, 14);
}
}Tip
コアクレートパターンにより、完全なTauriアプリをビルドせずにCIで cargo test を実行できます。高速で信頼性が高く、ロジックバグを早期にキャッチします。
Warning
cd core && cargo test がプロジェクト唯一のRustゲートになると、Tauri依存に触れるためアプリクレートへ追加された #[cfg(test)] モジュールは、CIでもpre-pushゲートでも定期再試験でも実行されないまま、黙って取り残されます。実際にzudo-text(Tauriデスクトップテキストエディタの非公開モノレポ)でこれが起きました。リダイレクト時のフェイルクローズ、SSE境界のパース、トークンの秘匿化を扱うセキュリティ上重要な7件のトランスポートテストが、どの自動化レベルにも含まれないアプリクレートに置かれていたのです(zudolab/zudo-text#5855)。
この穴は両面から塞いでください。純粋なロジックをコアクレートへ抽出するか、Tauriクレートをビルドできるランナーでアプリクレートのテストを明示的に実行する必要があります。また、コアクレートパターンを採用するときは、既存のテストが気付かないままゲートから消えないよう、アプリクレートに取り残された #[cfg(test)] モジュールがないか監査してください。
バックエンドブリッジモックアダプターパターン
Tauriアプリでは、フロントエンドはIPCコマンドを通じてRustバックエンドと通信します。フロントエンドテストでは、アダプター境界でこのブリッジをモックします。以下の createMockBackend() は、固定のデフォルト値と対象を絞ったオーバーライドを持つ関数テーブルという最小限の形であり、ユニットテストやコンポーネントテストに適しています。
// src/adapters/backend.ts -- the real adapter
import { invoke } from "@tauri-apps/api/core";
export interface BackendAdapter {
getSettings(): Promise<Settings>;
saveSettings(settings: Settings): Promise<void>;
readFile(path: string): Promise<string>;
}
export const tauriBackend: BackendAdapter = {
async getSettings() {
return invoke<Settings>("get_settings");
},
async saveSettings(settings) {
return invoke("save_settings", { settings });
},
async readFile(path) {
return invoke<string>("read_file", { path });
},
};// src/adapters/mock-backend.ts -- for testing
import type { BackendAdapter, Settings } from "./backend";
export function createMockBackend(
overrides: Partial<BackendAdapter> = {}
): BackendAdapter {
return {
async getSettings() {
return { theme: "dark", fontSize: 14 };
},
async saveSettings() {},
async readFile() {
return "mock file content";
},
...overrides,
};
}// In the app entry point
import { tauriBackend } from "./adapters/backend";
import { createMockBackend } from "./adapters/mock-backend";
// Runtime detection, not build-mode detection: import.meta.env.MODE === "test"
// only ever fires under Vitest. Playwright drives the built app in a real
// browser, so the mock must be selected by checking for the Tauri WebView's
// injected global -- otherwise E2E runs would hit the real IPC bridge.
const backend =
"__TAURI_INTERNALS__" in window ? tauriBackend : createMockBackend();パッケージ化されたTauriアプリでは、トランスポートはTauri IPCチャンネルの背後にあるRustネイティブのHTTPとSSEです。MSWのようなブラウザレベルのネットワークモックからはその通信が見えないため、アダプター境界は単なるテスト上の好みではなく、アーキテクチャによって強制される選択です。境界を選ぶ判断の全体像は、外部サービスのテスト境界を選ぶを参照してください。
フルジャーニーのe2eテストには、この最小限のスタブではなくステートフルなフェイクサービスが必要です。本物のサーバーと同じガードを維持し、決定論的な失敗制御を公開し、秘匿化済みのリクエスト証跡を記録するインメモリサービスです。対象を絞ったユニットテストやコンポーネントテストでは小さなアダプターを使い、一連のジャーニーで先行するリクエストがクライアントのステートマシンを介して後続のレスポンスを決めることまで証明するときは、ステートフルな形を使ってください。
実際のTauri境界を自動化する
ブラウザレーンとネイティブレーンが答える問いは異なります。dev serverに対するPlaywrightは、実ブラウザエンジン内のfrontend behaviorを証明しますが、Tauri IPC、native window creation、window間event delivery、AppKit focus、OSのclose handlingまでは証明しません。これらにはembedded WebDriverを組み込んだテスト専用Tauri bundleをビルドし、WebdriverIOから実際のWebViewを操作します。
ネイティブレーンはブラウザE2Eの代替ではなく、対象を絞ったintegration testとして扱ってください。
Rust WebDriver pluginをオプトインCargo featureと専用Tauri capability/configurationの背後に置く。
bundleには固有identifier、分離したfixture state、incognito WebView storeを与え、個人アカウントのcredentialを使わない。
固定driver portを所有する場合や実際のapplication windowを作成する場合はsuiteを直列化する。
W3C keyboard/pointer actionsを使う。DOMの
dispatchEvent()は、native focus、clipboard、editor input pathが動く証拠にはならない。失敗時には全WebDriver windowから秘匿化済みevidenceを保存し、その後fixture stateを消去して完全一致するtest-bundle processだけを終了する。
通常のproduction buildにdriver dependency、permission、alternate bootstrap、test markerが含まれないことを別途証明する。
このレーンは、renderer realmをまたぐregressionで特に有効です。jsdomテストでは別々のmodule graphと共有fake backendをモデル化できますが、実際のchild windowがhandbackを送り、hostがrestore前に適用し、OS closeがchildを正確に1回だけ破棄したことまで確立できるのはネイティブレーンだけです。
8ステップエスカレーションラダー
Tauriアプリの場合、エスカレーションラダーは標準のテストレベルを超えて拡張されます:
| ステップ | メソッド | キャッチするもの |
|---|---|---|
| 1 | コアクレートのcargo test | 純粋なRustロジックバグ |
| 2 | Vitestユニットテスト | フロントエンドロジックバグ |
| 3 | Vitest + jsdomコンポーネントテスト | コンポーネント動作バグ |
| 4 | Playwright WebKit(devサーバー) | フロントエンドレンダリングバグ |
| 5 | Playwright WebKit(本番ビルド) | ビルド固有のフロントエンドバグ |
| 6 | verify-ui + headless-browser | フロントエンドのCSS/ビジュアルバグ |
| 7 | テスト専用Tauri bundle + embedded WebDriver(またはdev modeの手動fallback) | IPC、native window、focus、close lifecycleのバグ |
| 8 | Tauri本番ビルドの手動テスト | 完全なアプリパッケージングバグ |
Note
ステップ1-6は広く自動化でき、CIに含めるべきです。ステップ7も、platform-capable runnerとfeature-isolated embedded WebDriverがあれば自動化できます。用意できない場合は手動テストへfallbackします。ステップ8はproduction bundleの最終inspectionとmanual journeyです。
ステップバイステップガイド
ステップ1-3: 高速、自動化可能、ブラウザ不要
# Step 1: Rust core logic
cd core && cargo test
# Step 2: Frontend unit tests
pnpm vitest --project unit
# Step 3: Frontend component tests
pnpm vitest --project componentステップ4-6: ブラウザが必要、ただし自動化可能
# Step 4: E2E against dev server (WebKit only)
# playwright.config.ts's webServer starts `pnpm dev` and waits for it to be
# ready -- no manual backgrounding, so Playwright can't race an unready server.
npx playwright test --project=webkit
# Step 5: E2E against production build
pnpm build
# webServer's command is `pnpm preview` here (see CI Configuration below);
# Playwright starts it and waits for port 4173 before any test runs.
npx playwright test --project=webkit
# Step 6: Visual verification -- extract computed styles + screenshots, then
# compare the JSON `styles` output against the expected values (verify-ui is
# a plain Node script, not a CLI with its own --check flag)
node $HOME/.claude/skills/verify-ui/scripts/verify-styles.mjs \
"http://localhost:4173" ".app" "./verify-ui-out" "400,800,1200" "light,dark"ステップ7-8: 完全なTauriアプリが必要
# Step 7: isolated native integration lane (project-specific command)
pnpm native-e2e:build
pnpm native-e2e:smoke
# If no native driver harness exists, test the same journey manually in tauri dev.
# Step 8: Production build
pnpm tauri build
# Test the built .dmg / .msi / .AppImageTauriプロジェクトの CI設定
ワークフローのステップ内でビルド+起動チェーンをバックグラウンド化する代わりに、webServer をすでにビルド済みのpreviewサーバーに向けてください——これは姉妹関係にあるPlaywrightページの本番ビルド検証パターンと同じ考え方です:
// playwright.config.ts (excerpt)
export default defineConfig({
webServer: {
command: "pnpm preview",
port: 4173,
reuseExistingServer: !process.env.CI,
},
// ...projects from "WebKit First: Matching the Production Engine" above
});# .github/workflows/test.yml
jobs:
rust-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: cd core && cargo test
frontend-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- run: pnpm install
- run: pnpm vitest run
e2e-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
- run: pnpm install
- run: npx playwright install webkit --with-deps
- run: pnpm build
- run: npx playwright test --project=webkitWarning
Tauri E2Eテストは、OSにかかわらずWebKitのブラウザバイナリのインストールが必要です——Playwrightは常に自身がバンドルしたWebKitビルドを操作するのであって、システムのSafariを使うことは決してありません。npx playwright install webkit はmacOSを含むすべてのランナーで必要です。Ubuntu固有なのは(WebKitが必要とするLinuxのシステムライブラリをインストールする)--with-deps フラグだけです。macOSランナーでは npx playwright install webkit だけで十分です。
セーフティネットとしての定期再試験
Tauriのキーボードショートカットやネイティブプラットフォームのテストはプラットフォーム非対応カテゴリに分類されます:実際のWebKit/macOSでのみ信頼できます。上記のCI設定はCIセーフなレイヤー(フロントエンドユニットテストとDOMのみのPlaywrightスペック)をカバーしていますが、薄いネイティブ統合レイヤー — @interactiveおよび@macos-onlyタグ付きスペック — にはセーフティネットとして定期的なmacOSジョブが必要です。
ローカルヘビーレーン(exam)は便利ですが回避可能です。また環境依存でもあります(同じスイートがLinux/WSL2ホストでは多数のスペックをフォールスレッドします)。macOSランナー上での定期CI再試験が、これらのプラットフォーム依存スペックを実際のリグレッションゲートの一部とするペーパートレイルを提供します。
完全なワークフロー(cron + workflow_dispatchスケルトン、重複排除されたIssue起票、マージ前オンデマンドディスパッチ)については定期再試験 & 夜間試験を参照してください。