Tauri Rust 测试指南(AI总结)

Tauri 项目的测试策略和普通 Rust 项目有所不同,核心原因是 Tauri 运行时(AppHandle、Window、State)在测试环境下无法直接构造。本文梳理一套实用的分层测试方案。


一、为什么 Tauri 测试比较特殊

Tauri 命令通常长这样:

#[tauri::command]
pub async fn create_post(
    state: State<'_, AppState>,
    payload: CreatePostPayload,
) -> Result<Post, String> {
    state.post_service.create(payload).await.map_err(|e| e.to_string())
}

其中 State<'_, AppState> 由 Tauri 运行时注入,测试代码里无法手动构造,直接写 #[test] 会编译失败或 panic。

另外在 Windows 上,cargo test 生成的二进制没有 Tauri 需要的 Windows manifest,会触发 STATUS_ENTRYPOINT_NOT_FOUND,这是已知的平台限制。

https://github.com/tauri-apps/tauri/issues/13419


二、分层测试策略

┌─────────────────────────────────┐
│         e2e 测试                 │  ← WebdriverIO / Selenium
│  Tauri 命令、emit、窗口交互       │
├─────────────────────────────────┤
│         集成测试                 │  ← cargo test (tests/ 目录)
│  数据库、文件系统、Service 层     │
├─────────────────────────────────┤
│         单元测试                 │  ← cargo test (同文件 mod tests)
│  纯业务逻辑、Domain 层、工具函数  │
└─────────────────────────────────┘

原则:把逻辑下沉,命令层只做薄薄的包装。


三、单元测试:测纯逻辑

把业务逻辑从命令里提取出来,写成普通函数:

// 业务逻辑:纯函数,不依赖 Tauri
pub fn validate_username(name: &str) -> Result<(), String> {
    if name.is_empty() {
        return Err("用户名不能为空".to_string());
    }
    if name.len() > 32 {
        return Err("用户名不能超过 32 个字符".to_string());
    }
    Ok(())
}

// Tauri 命令:只做调用
#[tauri::command]
pub fn check_username(name: String) -> Result<(), String> {
    validate_username(&name)
}

测试只测业务逻辑:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_empty_username() {
        assert!(validate_username("").is_err());
    }

    #[test]
    fn test_valid_username() {
        assert!(validate_username("alice").is_ok());
    }

    #[test]
    fn test_too_long_username() {
        let long_name = "a".repeat(33);
        assert!(validate_username(&long_name).is_err());
    }
}

四、集成测试:测 Service / 数据库

用内存数据库(SQLite :memory:)做集成测试,不依赖 Tauri 运行时:

// tests/post_service_test.rs

use my_app::infrastructure::database::ConnectionPool;
use my_app::application::services::PostService;

#[tokio::test]
async fn test_create_post() {
    // 用内存库,测试结束自动销毁
    let pool = ConnectionPool::new(":memory:").await.unwrap();
    pool.run_migrations().await.unwrap();

    let service = PostService::new(pool);
    let result = service.create("hello world").await;

    assert!(result.is_ok());
    assert_eq!(result.unwrap().content, "hello world");
}

Cargo.toml 里确保 tokio 开启 test feature:

[dev-dependencies]
tokio = { version = "1", features = ["full"] }

五、需要 AppHandle 时:用 tauri::test

Tauri 提供了 mock_builder 用于测试需要 AppHandle 的场景:

# Cargo.toml
[dev-dependencies]
tauri = { version = "2", features = ["test"] }
#[cfg(test)]
mod tests {
    use tauri::test::mock_builder;
    use tauri::Manager;

    #[test]
    fn test_state_injection() {
        let app = mock_builder()
            .build(tauri::generate_context!())
            .unwrap();

        app.manage(MyState { count: 0 });

        let state: tauri::State<MyState> = app.state();
        assert_eq!(state.count, 0);
    }
}

⚠️ Windows 上这种测试仍可能因 manifest 问题失败,建议放到 Linux CI 里跑。


六、e2e 测试:测 Tauri 命令本身

对于真正依赖运行时的部分(emit 事件、窗口操作、完整命令调用),用 WebdriverIO 驱动真实 Tauri 窗口。

Tauri 官方文档:https://tauri.app/develop/tests/webdriver/

基本流程:

  1. 安装 tauri-driver
  2. 配置 WebdriverIO
  3. 启动 Tauri app,通过 WebDriver 协议调用命令、断言结果
cargo install tauri-driver
// wdio.conf.js (简化)
export const config = {
    specs: ['./tests/e2e/**/*.spec.js'],
    capabilities: [{
        'tauri:options': {
            application: './src-tauri/target/release/my-app'
        }
    }],
}

七、Windows 上的特殊问题

在 Windows 原生环境跑 cargo test,如果项目依赖了 Tauri 运行时(即使是间接依赖),会出现:

error: process didn't exit successfully: STATUS_ENTRYPOINT_NOT_FOUND

根本原因tao(Tauri 的窗口库)静态链接了 comctl32.dll v6,需要 Windows manifest 激活,但 cargo test 生成的二进制没有这个 manifest。

解决方案

方案 说明
用 Linux/Docker 跑测试 最稳定,推荐 CI 使用
#[cfg(not(test))] 隔离 Tauri 依赖 让测试二进制不链接 Tauri 运行时
只测不依赖运行时的层 根本解法,架构上分离

项目里常见的隔离写法:

// lib.rs
#[cfg(not(test))]
mod state;  // state 模块依赖 AppHandle,测试时不编译

#[cfg(not(test))]
mod tauri_app;

八、快速参考

测什么 用什么 能在 Windows 跑吗
纯函数、Domain 逻辑 #[test]
异步 Service #[tokio::test]
数据库操作 #[tokio::test] + 内存库
需要 AppHandle tauri::test::mock_builder ⚠️ 不稳定
Tauri 命令、emit、窗口 WebdriverIO e2e ✅(需打包)

九、总结

Tauri 测试的核心思路只有一句话:

把业务逻辑从 Tauri 命令里分离出来,命令层只做参数传递,逻辑层用普通 Rust 测试覆盖,运行时相关的用 e2e 兜底。

这样既能保证测试覆盖率,又不被 Tauri 运行时的限制卡住。

posted @ 2026-03-23 02:36  丘狸尾  阅读(99)  评论(0)    收藏  举报