0
# Testing & Mocks
1
2
Mock implementations and testing utilities for developing and testing Tauri applications without requiring the full Tauri runtime environment.
3
4
## Capabilities
5
6
### IPC Mocking
7
8
Mock the core IPC (Inter-Process Communication) system to simulate backend responses during testing.
9
10
```typescript { .api }
11
/**
12
* Mock the IPC system with custom command handlers
13
* @param cb - Function to handle mocked IPC commands
14
* @param options - Optional configuration for mocking behavior
15
*/
16
function mockIPC(
17
cb: (cmd: string, payload?: InvokeArgs) => unknown,
18
options?: MockIPCOptions
19
): void;
20
21
interface MockIPCOptions {
22
/** Whether to mock event system as well */
23
shouldMockEvents?: boolean;
24
}
25
26
type InvokeArgs = Record<string, unknown> | number[] | ArrayBuffer | Uint8Array;
27
```
28
29
### Window Mocking
30
31
Mock window instances and labels for testing window-related functionality.
32
33
```typescript { .api }
34
/**
35
* Mock window instances for testing
36
* @param current - Label of the current window
37
* @param additionalWindows - Labels of additional windows to mock
38
*/
39
function mockWindows(current: string, ...additionalWindows: string[]): void;
40
```
41
42
### File Source Conversion Mocking
43
44
Mock file path to URL conversion for testing asset loading.
45
46
```typescript { .api }
47
/**
48
* Mock file source conversion for testing
49
* @param osName - Operating system name to simulate ('linux', 'darwin', 'windows')
50
*/
51
function mockConvertFileSrc(osName: string): void;
52
```
53
54
### Mock Cleanup
55
56
Clear all active mocks and restore normal Tauri behavior.
57
58
```typescript { .api }
59
/**
60
* Clear all mocks and restore normal Tauri functionality
61
*/
62
function clearMocks(): void;
63
```
64
65
## Usage Examples
66
67
### Basic IPC Mocking
68
69
```typescript
70
import { mockIPC, invoke } from '@tauri-apps/api/core';
71
import { clearMocks } from '@tauri-apps/api/mocks';
72
73
// Mock IPC commands for testing
74
mockIPC((cmd, args) => {
75
switch (cmd) {
76
case 'get_user_data':
77
return { name: 'Test User', email: 'test@example.com' };
78
case 'save_file':
79
console.log('Mock: Saving file', args);
80
return { success: true, path: '/mock/path/file.txt' };
81
case 'calculate':
82
const { a, b } = args as { a: number; b: number };
83
return { result: a + b };
84
default:
85
throw new Error(`Unknown command: ${cmd}`);
86
}
87
});
88
89
// Test the application code
90
async function testUserData() {
91
const userData = await invoke('get_user_data');
92
console.log('User data:', userData); // { name: 'Test User', email: 'test@example.com' }
93
94
const saveResult = await invoke('save_file', { content: 'Hello', filename: 'test.txt' });
95
console.log('Save result:', saveResult); // { success: true, path: '/mock/path/file.txt' }
96
97
const calcResult = await invoke('calculate', { a: 5, b: 3 });
98
console.log('Calculation:', calcResult); // { result: 8 }
99
}
100
101
await testUserData();
102
103
// Clean up mocks
104
clearMocks();
105
```
106
107
### Testing with Mock Events
108
109
```typescript
110
import { mockIPC } from '@tauri-apps/api/mocks';
111
import { listen, emit } from '@tauri-apps/api/event';
112
113
// Mock IPC with events enabled
114
mockIPC((cmd, args) => {
115
if (cmd === 'start_background_task') {
116
// Simulate background task that emits progress events
117
setTimeout(() => emit('task-progress', { progress: 0.5 }), 100);
118
setTimeout(() => emit('task-complete', { result: 'done' }), 200);
119
return { taskId: 'mock-task-123' };
120
}
121
return null;
122
}, { shouldMockEvents: true });
123
124
// Test event-driven functionality
125
async function testBackgroundTask() {
126
// Set up event listeners
127
const progressListener = await listen('task-progress', (event) => {
128
console.log('Progress:', event.payload.progress);
129
});
130
131
const completeListener = await listen('task-complete', (event) => {
132
console.log('Task complete:', event.payload.result);
133
progressListener(); // Clean up listener
134
completeListener();
135
});
136
137
// Start the task
138
const result = await invoke('start_background_task', { data: 'test' });
139
console.log('Task started:', result);
140
}
141
142
await testBackgroundTask();
143
clearMocks();
144
```
145
146
### Window Mocking for Testing
147
148
```typescript
149
import { mockWindows } from '@tauri-apps/api/mocks';
150
import { getCurrentWindow, Window } from '@tauri-apps/api/window';
151
152
// Mock multiple windows
153
mockWindows('main-window', 'settings-window', 'about-window');
154
155
async function testWindowFunctionality() {
156
// Get current window (will be 'main-window')
157
const currentWindow = getCurrentWindow();
158
console.log('Current window label:', currentWindow.label); // 'main-window'
159
160
// Get all windows
161
const allWindows = await Window.getAll();
162
console.log('All windows:', allWindows.map(w => w.label));
163
// ['main-window', 'settings-window', 'about-window']
164
165
// Get specific window
166
const settingsWindow = await Window.getByLabel('settings-window');
167
console.log('Settings window found:', settingsWindow !== null); // true
168
169
// Test window operations (these will be no-ops in mock)
170
await currentWindow.setTitle('Test Title');
171
await currentWindow.setSize({ width: 800, height: 600 });
172
}
173
174
await testWindowFunctionality();
175
clearMocks();
176
```
177
178
### File Source Mocking
179
180
```typescript
181
import { mockConvertFileSrc } from '@tauri-apps/api/mocks';
182
import { convertFileSrc } from '@tauri-apps/api/core';
183
184
// Mock file conversion for different platforms
185
mockConvertFileSrc('linux');
186
187
function testFileConversion() {
188
const linuxPath = convertFileSrc('/home/user/image.png');
189
console.log('Linux path:', linuxPath); // Mock Linux-style conversion
190
191
clearMocks();
192
mockConvertFileSrc('windows');
193
194
const windowsPath = convertFileSrc('C:\\Users\\User\\image.png');
195
console.log('Windows path:', windowsPath); // Mock Windows-style conversion
196
197
clearMocks();
198
mockConvertFileSrc('darwin');
199
200
const macPath = convertFileSrc('/Users/user/image.png');
201
console.log('macOS path:', macPath); // Mock macOS-style conversion
202
}
203
204
testFileConversion();
205
clearMocks();
206
```
207
208
### Jest Testing Integration
209
210
```typescript
211
// test-utils.ts
212
import { mockIPC, mockWindows, clearMocks } from '@tauri-apps/api/mocks';
213
214
export function setupTauriMocks() {
215
// Mock basic IPC commands
216
mockIPC((cmd, args) => {
217
switch (cmd) {
218
case 'get_config':
219
return { theme: 'dark', language: 'en' };
220
case 'set_config':
221
return { success: true };
222
case 'get_version':
223
return '1.0.0';
224
default:
225
return null;
226
}
227
});
228
229
// Mock windows
230
mockWindows('main');
231
}
232
233
export function teardownTauriMocks() {
234
clearMocks();
235
}
236
237
// app.test.ts
238
import { setupTauriMocks, teardownTauriMocks } from './test-utils';
239
import { invoke } from '@tauri-apps/api/core';
240
241
describe('Application Tests', () => {
242
beforeEach(() => {
243
setupTauriMocks();
244
});
245
246
afterEach(() => {
247
teardownTauriMocks();
248
});
249
250
test('should load configuration', async () => {
251
const config = await invoke('get_config');
252
expect(config).toEqual({ theme: 'dark', language: 'en' });
253
});
254
255
test('should save configuration', async () => {
256
const result = await invoke('set_config', { theme: 'light' });
257
expect(result).toEqual({ success: true });
258
});
259
260
test('should get version', async () => {
261
const version = await invoke('get_version');
262
expect(version).toBe('1.0.0');
263
});
264
});
265
```
266
267
### Advanced Mock Scenarios
268
269
```typescript
270
import { mockIPC } from '@tauri-apps/api/mocks';
271
272
// Mock complex async operations
273
mockIPC(async (cmd, args) => {
274
switch (cmd) {
275
case 'fetch_data':
276
// Simulate network delay
277
await new Promise(resolve => setTimeout(resolve, 100));
278
return { data: ['item1', 'item2', 'item3'] };
279
280
case 'upload_file':
281
// Simulate file upload with progress
282
const { file } = args as { file: string };
283
await new Promise(resolve => setTimeout(resolve, 50));
284
return {
285
success: true,
286
url: `https://example.com/files/${file}`,
287
size: 1024
288
};
289
290
case 'database_query':
291
// Simulate database operations
292
const { query } = args as { query: string };
293
if (query.includes('SELECT')) {
294
return { rows: [{ id: 1, name: 'Test' }] };
295
} else if (query.includes('INSERT')) {
296
return { insertedId: 42 };
297
}
298
throw new Error('Invalid query');
299
300
default:
301
throw new Error(`Command not mocked: ${cmd}`);
302
}
303
});
304
305
// Test error scenarios
306
async function testErrorHandling() {
307
try {
308
await invoke('invalid_command');
309
} catch (error) {
310
console.log('Expected error:', error.message); // "Command not mocked: invalid_command"
311
}
312
313
try {
314
await invoke('database_query', { query: 'DROP TABLE users' });
315
} catch (error) {
316
console.log('Database error:', error.message); // "Invalid query"
317
}
318
}
319
320
await testErrorHandling();
321
```
322
323
### Mock State Management
324
325
```typescript
326
import { mockIPC } from '@tauri-apps/api/mocks';
327
328
// Create stateful mocks
329
class MockDatabase {
330
private data: Record<string, any> = {};
331
332
get(key: string) {
333
return this.data[key] || null;
334
}
335
336
set(key: string, value: any) {
337
this.data[key] = value;
338
return { success: true };
339
}
340
341
list() {
342
return Object.keys(this.data);
343
}
344
345
clear() {
346
this.data = {};
347
return { success: true };
348
}
349
}
350
351
const mockDb = new MockDatabase();
352
353
mockIPC((cmd, args) => {
354
switch (cmd) {
355
case 'db_get':
356
const { key } = args as { key: string };
357
return mockDb.get(key);
358
359
case 'db_set':
360
const { key: setKey, value } = args as { key: string; value: any };
361
return mockDb.set(setKey, value);
362
363
case 'db_list':
364
return mockDb.list();
365
366
case 'db_clear':
367
return mockDb.clear();
368
369
default:
370
return null;
371
}
372
});
373
374
// Test stateful operations
375
async function testStatefulMocks() {
376
// Set some data
377
await invoke('db_set', { key: 'user:1', value: { name: 'Alice' } });
378
await invoke('db_set', { key: 'user:2', value: { name: 'Bob' } });
379
380
// Retrieve data
381
const user1 = await invoke('db_get', { key: 'user:1' });
382
console.log('User 1:', user1); // { name: 'Alice' }
383
384
// List keys
385
const keys = await invoke('db_list');
386
console.log('Keys:', keys); // ['user:1', 'user:2']
387
388
// Clear all data
389
await invoke('db_clear');
390
const keysAfterClear = await invoke('db_list');
391
console.log('Keys after clear:', keysAfterClear); // []
392
}
393
394
await testStatefulMocks();
395
```
396
397
### Testing Best Practices
398
399
```typescript
400
import { mockIPC, mockWindows, clearMocks } from '@tauri-apps/api/mocks';
401
402
// Utility for consistent mock setup
403
export class TauriTestEnvironment {
404
private mockHandlers = new Map<string, Function>();
405
406
mockCommand(cmd: string, handler: Function) {
407
this.mockHandlers.set(cmd, handler);
408
}
409
410
setup() {
411
mockIPC((cmd, args) => {
412
const handler = this.mockHandlers.get(cmd);
413
if (handler) {
414
return handler(args);
415
}
416
throw new Error(`Unmocked command: ${cmd}`);
417
});
418
419
mockWindows('main-window');
420
}
421
422
teardown() {
423
clearMocks();
424
this.mockHandlers.clear();
425
}
426
}
427
428
// Usage in tests
429
describe('Feature Tests', () => {
430
let testEnv: TauriTestEnvironment;
431
432
beforeEach(() => {
433
testEnv = new TauriTestEnvironment();
434
435
// Set up common mocks
436
testEnv.mockCommand('get_config', () => ({ theme: 'light' }));
437
testEnv.mockCommand('save_config', () => ({ success: true }));
438
439
testEnv.setup();
440
});
441
442
afterEach(() => {
443
testEnv.teardown();
444
});
445
446
test('feature specific test', async () => {
447
// Add feature-specific mocks
448
testEnv.mockCommand('feature_command', (args) => {
449
return { result: `processed ${args.input}` };
450
});
451
452
// Test the feature
453
const result = await invoke('feature_command', { input: 'test' });
454
expect(result.result).toBe('processed test');
455
});
456
});
457
```
458
459
## Mock Limitations
460
461
The mocking system has some limitations to be aware of:
462
463
- **Window operations**: Mocked windows don't actually create system windows
464
- **File system**: File operations still need proper mocking at the command level
465
- **Platform APIs**: Platform-specific features may not be fully mockable
466
- **Event timing**: Event emission timing is simplified in mocks
467
- **Resource management**: Mocked resources don't have the same lifecycle as real ones
468
469
For comprehensive testing, combine Tauri mocks with other testing tools and consider running integration tests with the actual Tauri runtime when possible.