You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为使用SQLite数据库的Tauri应用编写单元测试?

解决方案:Tauri + Vitest 测试 SQLite 交互逻辑

针对你遇到的Vitest无法直接访问Tauri Rust侧SQL操作的问题,以下是可行的解决方案,包括对你两种思路的优化和其他替代方案:

优化你的现有思路

1. 启动传参的测试方案

不用在前端代码里直接调用describe/test,而是通过Tauri的条件编译+官方测试命令实现:

  • 在Rust的main.rs中检测命令行参数,比如--test-db,如果存在则启用测试模式,初始化测试数据库并暴露测试接口;
  • 生产环境通过条件编译标记(#[cfg(not(test))])排除测试相关代码,确保生产包干净;
  • 使用Tauri官方提供的tauri test命令,配合Vitest的tauri环境配置,直接启动测试模式的Tauri实例运行前端测试,不会丢失上下文:
// vitest.config.ts
export default defineConfig({
  test: {
    environment: 'tauri',
  },
})

2. 抽象接口适配不同SQL客户端

这个依赖倒置的思路非常实用,目前没有现成的通用包装类,但实现起来很简单:
首先定义抽象接口,包含你用到的所有SQL操作方法:

export interface DatabaseClient {
  execute(sql: string, params?: any[]): Promise<number>;
  select<T>(sql: string, params?: any[]): Promise<T[]>;
  close(): Promise<void>;
}

然后实现Tauri版本的客户端:

import { Database } from '@tauri-apps/plugin-sql';

export class TauriSqlClient implements DatabaseClient {
  private db: Database | null = null;

  async init(connectionString: string) {
    this.db = await Database.load(connectionString);
  }

  async execute(sql: string, params?: any[]): Promise<number> {
    if (!this.db) throw new Error('Database not initialized');
    return this.db.execute(sql, params);
  }

  async select<T>(sql: string, params?: any[]): Promise<T[]> {
    if (!this.db) throw new Error('Database not initialized');
    return this.db.select<T>(sql, params);
  }

  async close(): Promise<void> {
    if (this.db) await this.db.close();
  }
}

再实现Node sqlite3版本的客户端(以better-sqlite3为例):

import Database from 'better-sqlite3';

export class NodeSqlClient implements DatabaseClient {
  private db: Database | null = null;

  async init(connectionString: string) {
    this.db = new Database(connectionString.includes(':memory:') ? ':memory:' : connectionString);
  }

  async execute(sql: string, params?: any[]): Promise<number> {
    if (!this.db) throw new Error('Database not initialized');
    const stmt = this.db.prepare(sql);
    return stmt.run(params || []).changes;
  }

  async select<T>(sql: string, params?: any[]): Promise<T[]> {
    if (!this.db) throw new Error('Database not initialized');
    const stmt = this.db.prepare(sql);
    return stmt.all(params || []) as T[];
  }

  async close(): Promise<void> {
    if (this.db) this.db.close();
  }
}

测试时注入NodeSqlClient,生产时注入TauriSqlClient,业务逻辑完全不需要关心底层实现。

其他可行方案

1. 直接在Rust侧写SQL测试

把核心的SQL操作逻辑封装到Rust的函数或模块中,用Rust的cargo test直接测试这些逻辑,比如:

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

    #[test]
    fn test_insert_and_query() {
        let sql = Sql::default();
        let db = sql.load("sqlite::memory:").unwrap();
        db.execute("CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)", []).unwrap();
        db.execute("INSERT INTO test (name) VALUES (?)", ["test"]).unwrap();
        let rows: Vec<(i32, String)> = db.select("SELECT id, name FROM test", []).unwrap();
        assert_eq!(rows.len(), 1);
        assert_eq!(rows[0].1, "test");
    }
}

前端只需要测试调用这些Rust函数的逻辑,用@tauri-apps/api/mocks模拟API即可。

2. 使用内存数据库加速测试

不管是用Tauri的sqlite:memory:还是Node的内存数据库,在测试时使用内存实例,避免操作真实文件,测试速度更快,且不会留下测试数据。配合Vitest的钩子函数初始化:

let client: DatabaseClient;

beforeEach(async () => {
  client = new NodeSqlClient(); // 测试用Node客户端
  await client.init('sqlite::memory:');
  // 初始化测试表结构
  await client.execute('CREATE TABLE test (id INTEGER PRIMARY KEY, name TEXT)');
});

afterEach(async () => {
  await client.close();
});

内容的提问来源于stack exchange,提问作者Frank Müller

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 20:42:42