跳到內容
Tauri

模擬 Tauri API

在編寫前端測試時,擁有一個「假的」Tauri 環境來模擬視窗或攔截 IPC 呼叫是很常見的,也就是所謂的模擬 (mocking)@tauri-apps/api/mocks 模組提供了一些有用的工具,讓您更容易做到這一點

IPC 請求

最常見的情況是,您想要攔截 IPC 請求;這在各種情況下都很有幫助

  • 確保進行正確的後端呼叫
  • 模擬來自後端函數的不同結果

Tauri 提供了 mockIPC 函數來攔截 IPC 請求。您可以在這裡找到有關特定 API 的詳細資訊。

import { beforeAll, expect, test } from "vitest";
import { randomFillSync } from "crypto";
import { mockIPC } from "@tauri-apps/api/mocks";
import { invoke } from "@tauri-apps/api/core";
// jsdom doesn't come with a WebCrypto implementation
beforeAll(() => {
Object.defineProperty(window, 'crypto', {
value: {
// @ts-ignore
getRandomValues: (buffer) => {
return randomFillSync(buffer);
},
},
});
});
test("invoke simple", async () => {
mockIPC((cmd, args) => {
// simulated rust command called "add" that just adds two numbers
if(cmd === "add") {
return (args.a as number) + (args.b as number);
}
});
});

有時您想要追蹤有關 IPC 呼叫的更多資訊;命令被調用了多少次?它是否被調用過?您可以將 mockIPC() 與其他 spying 和 mocking 工具一起使用來測試這一點

import { beforeAll, expect, test, vi } from "vitest";
import { randomFillSync } from "crypto";
import { mockIPC } from "@tauri-apps/api/mocks";
import { invoke } from "@tauri-apps/api/core";
// jsdom doesn't come with a WebCrypto implementation
beforeAll(() => {
Object.defineProperty(window, 'crypto', {
value: {
// @ts-ignore
getRandomValues: (buffer) => {
return randomFillSync(buffer);
},
},
});
});
test("invoke", async () => {
mockIPC((cmd, args) => {
// simulated rust command called "add" that just adds two numbers
if(cmd === "add") {
return (args.a as number) + (args.b as number);
}
});
// we can use the spying tools provided by vitest to track the mocked function
const spy = vi.spyOn(window.__TAURI_INTERNALS__, "invoke");
expect(invoke("add", { a: 12, b: 15 })).resolves.toBe(27);
expect(spy).toHaveBeenCalled();
});

若要模擬對 sidecar 或 shell 命令的 IPC 請求,您需要在呼叫 spawn()execute() 時取得事件處理程式的 ID,並使用此 ID 發出後端會發回的事件

mockIPC(async (cmd, args) => {
if (args.message.cmd === 'execute') {
const eventCallbackId = `_${args.message.onEventFn}`;
const eventEmitter = window[eventCallbackId];
// 'Stdout' event can be called multiple times
eventEmitter({
event: 'Stdout',
payload: 'some data sent from the process',
});
// 'Terminated' event must be called at the end to resolve the promise
eventEmitter({
event: 'Terminated',
payload: {
code: 0,
signal: 'kill',
},
});
}
});

Windows

有時您有特定於視窗的程式碼(例如,啟動畫面視窗),因此您需要模擬不同的視窗。您可以使用 mockWindows() 方法來建立假的視窗標籤。第一個字串識別「目前」視窗(即,您的 JavaScript 認為自身所在的視窗),而所有其他字串都被視為額外視窗。

import { beforeAll, expect, test } from 'vitest';
import { randomFillSync } from 'crypto';
import { mockWindows } from '@tauri-apps/api/mocks';
// jsdom doesn't come with a WebCrypto implementation
beforeAll(() => {
Object.defineProperty(window, 'crypto', {
value: {
// @ts-ignore
getRandomValues: (buffer) => {
return randomFillSync(buffer);
},
},
});
});
test('invoke', async () => {
mockWindows('main', 'second', 'third');
const { getCurrent, getAll } = await import('@tauri-apps/api/webviewWindow');
expect(getCurrent()).toHaveProperty('label', 'main');
expect(getAll().map((w) => w.label)).toEqual(['main', 'second', 'third']);
});

© 2025 Tauri Contributors。CC-BY / MIT