Our review
This skill guides you through testing Tauri applications with unit tests using mock runtime, end-to-end tests via WebDriver, and CI integration.
Strengths
- Covers all major Tauri testing strategies: unit, mock, E2E, and CI
- Provides ready-to-use code snippets for Vitest and WebDriverIO
- Includes environment setup for Linux and Windows
Limitations
- Does not cover testing of custom Tauri plugins
- No WebDriver support for macOS
- Assumes familiarity with JavaScript testing frameworks
When building a Tauri application that requires reliable testing of both frontend and native API interactions.
For trivial apps with no native backend logic or when working exclusively on macOS without alternative E2E tooling.
Security analysis
SafeThe skill provides testing guidance for Tauri applications and contains no executable code beyond instructional shell commands; no destructive, obfuscated, or exfiltrating behavior.
No concerns found
Examples
Show me how to mock Tauri IPC commands for unit testing with Vitest, including setup and verification.How do I set up WebDriver end-to-end testing for my Tauri v2 app using WebDriverIO and tauri-driver on Windows?Help me add Tauri unit and E2E tests to a GitHub Actions workflow, including dependencies and run steps.name: testing-tauri-apps description: Guides developers through testing Tauri applications including unit testing with mock runtime, mocking Tauri APIs, WebDriver end-to-end testing with Selenium and WebdriverIO, and CI integration with GitHub Actions.
Testing Tauri Applications
This skill covers testing strategies for Tauri v2 applications: unit testing with mocks, end-to-end testing with WebDriver, and CI integration.
Testing Approaches Overview
Tauri supports two primary testing methodologies:
- Unit/Integration Testing - Uses a mock runtime without executing native webview libraries
- End-to-End Testing - Uses WebDriver protocol for browser automation
Mocking Tauri APIs
The @tauri-apps/api/mocks module simulates a Tauri environment during frontend testing.
Install Mock Dependencies
npm install -D vitest @tauri-apps/api
Mock IPC Commands
import { mockIPC, clearMocks } from '@tauri-apps/api/mocks';
import { invoke } from '@tauri-apps/api/core';
import { vi, describe, it, expect, afterEach } from 'vitest';
afterEach(() => {
clearMocks();
});
describe('Tauri Commands', () => {
it('should mock the add command', async () => {
mockIPC((cmd, args) => {
if (cmd === 'add') {
return (args.a as number) + (args.b as number);
}
});
const result = await invoke('add', { a: 12, b: 15 });
expect(result).toBe(27);
});
it('should verify invoke was called', async () => {
mockIPC((cmd) => {
if (cmd === 'greet') return 'Hello!';
});
const spy = vi.spyOn(window.__TAURI_INTERNALS__, 'invoke');
await invoke('greet', { name: 'World' });
expect(spy).toHaveBeenCalled();
});
});
Mock Sidecar and Shell Commands
import { mockIPC } from '@tauri-apps/api/mocks';
mockIPC(async (cmd, args) => {
if (args.message.cmd === 'execute') {
const eventCallbackId = `_${args.message.onEventFn}`;
const eventEmitter = window[eventCallbackId];
eventEmitter({ event: 'Stdout', payload: 'process output data' });
eventEmitter({ event: 'Terminated', payload: { code: 0 } });
}
});
Mock Events (v2.7.0+)
import { mockIPC } from '@tauri-apps/api/mocks';
import { emit, listen } from '@tauri-apps/api/event';
mockIPC(() => {}, { shouldMockEvents: true });
const eventHandler = vi.fn();
await listen('test-event', eventHandler);
await emit('test-event', { foo: 'bar' });
expect(eventHandler).toHaveBeenCalled();
Mock Windows
import { mockWindows } from '@tauri-apps/api/mocks';
import { getCurrent, getAll } from '@tauri-apps/api/webviewWindow';
mockWindows('main', 'second', 'third');
// First parameter is the "current" window
expect(getCurrent()).toHaveProperty('label', 'main');
expect(getAll().map((w) => w.label)).toEqual(['main', 'second', 'third']);
Vitest Configuration
// vitest.config.js
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'jsdom',
setupFiles: ['./test/setup.js'],
},
});
// test/setup.js
window.__TAURI_INTERNALS__ = {
invoke: vi.fn(),
transformCallback: vi.fn(),
};
WebDriver End-to-End Testing
WebDriver testing uses tauri-driver to automate Tauri applications.
Platform Support
| Platform | Support | Notes | |----------|---------|-------| | Windows | Full | Requires Microsoft Edge Driver | | Linux | Full | Requires WebKitWebDriver | | macOS | None | WKWebView lacks WebDriver tooling |
Install tauri-driver
cargo install tauri-driver --locked
Platform Dependencies
# Linux (Debian/Ubuntu)
sudo apt install webkit2gtk-driver xvfb
which WebKitWebDriver # Verify installation
# Windows (PowerShell)
cargo install --git https://github.com/chippers/msedgedriver-tool
& "$HOME/.cargo/bin/msedgedriver-tool.exe"
WebdriverIO Setup
Project Structure
my-tauri-app/
├── src-tauri/
├── src/
└── e2e-tests/
├── package.json
├── wdio.conf.js
└── specs/
└── app.spec.js
Package Configuration
{
"name": "tauri-e2e-tests",
"version": "1.0.0",
"type": "module",
"scripts": { "test": "wdio run wdio.conf.js" },
"dependencies": { "@wdio/cli": "^9.19.0" },
"devDependencies": {
"@wdio/local-runner": "^9.19.0",
"@wdio/mocha-framework": "^9.19.0",
"@wdio/spec-reporter": "^9.19.0"
}
}
WebdriverIO Configuration
// e2e-tests/wdio.conf.js
import { spawn, spawnSync } from 'child_process';
let tauriDriver;
export const config = {
hostname: '127.0.0.1',
port: 4444,
specs: ['./specs/**/*.js'],
maxInstances: 1,
capabilities: [{
browserName: 'wry',
'tauri:options': {
application: '../src-tauri/target/debug/my-tauri-app',
},
}],
framework: 'mocha',
reporters: ['spec'],
mochaOpts: { ui: 'bdd', timeout: 60000 },
onPrepare: () => {
const result = spawnSync('cargo', ['build', '--manifest-path', '../src-tauri/Cargo.toml'], {
stdio: 'inherit',
});
if (result.status !== 0) throw new Error('Failed to build Tauri app');
},
beforeSession: () => {
tauriDriver = spawn('tauri-driver', [], { stdio: ['ignore', 'pipe', 'pipe'] });
return new Promise((resolve) => {
tauriDriver.stdout.on('data', (data) => {
if (data.toString().includes('listening')) resolve();
});
});
},
afterSession: () => tauriDriver?.kill(),
};
WebdriverIO Test Example
// e2e-tests/specs/app.spec.js
describe('My Tauri App', () => {
it('should display the header', async () => {
const header = await $('body > h1');
expect(await header.getText()).toMatch(/^[hH]ello/);
});
it('should interact with a button', async () => {
const button = await $('#greet-button');
await button.click();
const output = await $('#greet-output');
await output.waitForExist({ timeout: 5000 });
expect(await output.getText()).toContain('Hello');
});
});
Selenium Setup
Package Configuration
{
"name": "tauri-selenium-tests",
"version": "1.0.0",
"scripts": { "test": "mocha" },
"dependencies": {
"chai": "^5.2.1",
"mocha": "^11.7.1",
"selenium-webdriver": "^4.34.0"
}
}
Selenium Test Example
// e2e-tests/test/test.js
import { spawn, spawnSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
import { Builder, By } from 'selenium-webdriver';
import { expect } from 'chai';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
let driver, tauriDriver;
const application = path.resolve(__dirname, '../../src-tauri/target/debug/my-tauri-app');
describe('Tauri App Tests', function () {
this.timeout(60000);
before(async function () {
spawnSync('cargo', ['build', '--manifest-path', '../../src-tauri/Cargo.toml'], {
cwd: __dirname, stdio: 'inherit',
});
tauriDriver = spawn('tauri-driver', [], { stdio: ['ignore', 'pipe', 'pipe'] });
await new Promise((resolve) => {
tauriDriver.stdout.on('data', (data) => {
if (data.toString().includes('listening')) resolve();
});
});
driver = await new Builder()
.usingServer('http://127.0.0.1:4444/')
.withCapabilities({ browserName: 'wry', 'tauri:options': { application } })
.build();
});
after(async function () {
await driver?.quit();
tauriDriver?.kill();
});
it('should display greeting', async function () {
const header = await driver.findElement(By.css('body > h1'));
expect(await header.getText()).to.match(/^[hH]ello/);
});
it('should click button and show output', async function () {
const button = await driver.findElement(By.id('greet-button'));
await button.click();
const output = await driver.findElement(By.id('greet-output'));
expect(await output.getText()).to.include('Hello');
});
});
CI Integration with GitHub Actions
# .github/workflows/e2e-tests.yml
name: E2E Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- name: Install Linux dependencies
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev build-essential \
curl wget file libxdo-dev libssl-dev \
libayatana-appindicator3-dev librsvg2-dev \
webkit2gtk-driver xvfb
- uses: dtolnay/rust-action@stable
- run: cargo install tauri-driver --locked
- name: Setup Windows WebDriver
if: matrix.os == 'windows-latest'
shell: pwsh
run: |
cargo install --git https://github.com/chippers/msedgedriver-tool
& "$HOME/.cargo/bin/msedgedriver-tool.exe"
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: npm run build
- run: cargo build --manifest-path src-tauri/Cargo.toml
- name: Run E2E tests (Linux)
if: matrix.os == 'ubuntu-latest'
working-directory: e2e-tests
run: npm install && xvfb-run npm test
- name: Run E2E tests (Windows)
if: matrix.os == 'windows-latest'
working-directory: e2e-tests
run: npm install && npm test
Best Practices
Mock Testing
- Always call
clearMocks()inafterEachto prevent state leakage - Use spies to verify IPC calls were made correctly
- Mock at the right level: IPC for commands, windows for multi-window logic
WebDriver Testing
- Use debug builds for faster iteration during development
- Set appropriate timeouts as Tauri apps may need time to initialize
- Wait for elements explicitly rather than using implicit waits
- Keep tests independent so each test works in isolation
CI Integration
- Use
xvfb-runon Linux for headless WebDriver testing - Match Edge Driver version on Windows to avoid connection issues
- Build the app before running WebDriver tests
- Run unit tests before e2e tests to catch issues early
Troubleshooting
WebDriver Connection Timeout
- Windows: Verify Edge Driver version matches installed Edge
- Linux: Ensure
webkit2gtk-driveris installed - Check
tauri-driveris running and listening on port 4444
Mock Not Working
- Import
@tauri-apps/api/mocksbefore the code under test - Call
clearMocks()inafterEachto reset state - Ensure
window.__TAURI_INTERNALS__is properly mocked in setup
CI Failures
- Linux: Add
xvfb-runprefix to test commands - Windows: Install Edge Driver via
msedgedriver-tool - Increase timeout for slower CI runners
References
TDD Red-Green-Refactor
Testing
Skill that guides Claude through the complete TDD cycle.
Web Accessibility Audit
Testing
Performs a comprehensive web accessibility audit following WCAG standards.
UAT Test Case Generator
Testing
Generates structured and comprehensive user acceptance test cases.