Test Types
Unit Tests
Unit tests verify individual components and functions in isolation.Rust Unit Tests
Rust unit tests are embedded within the source code using Rust’s built-in testing framework. Run all Rust unit tests:Cargo.toml workspaces with common testing libraries like proptest for property-based testing and insta for snapshot testing.
Desktop Unit Tests (JavaScript/TypeScript)
Unit tests for the desktop application use Vitest as the test runner. Run desktop unit tests:desktop/packages/mullvad-vpn/test/unit/ and test utilities, helpers, and business logic:
- Account validation (
account-number.spec.ts) - Date and time helpers (
date-helper.spec.ts) - IP address utilities (
ip.spec.ts) - Tunnel state management (
tunnel-state.spec.ts) - Notification logic (
notification-evaluation.spec.ts)
package.json:
Android Unit Tests
Android unit tests use JUnit5 and are located in module-specifictest/ directories.
Run Android unit tests:
app/build.gradle.kts:
iOS Unit Tests
iOS unit tests use XCTest and are organized into test targets. Run iOS tests via Xcode:MullvadRESTTests- API and REST client testsMullvadRustRuntimeTests- Rust FFI integration testsMullvadPostQuantumTests- Post-quantum cryptography tests
Integration Tests
Integration tests verify interactions between components and with external services.Rust Integration Tests
The test workspace (test/ directory) contains a dedicated testing framework for end-to-end integration tests.
Test workspace structure:
test-manager- Client that runs on the host and orchestrates teststest-runner- Server that runs in guest VMs and provides RPC interfacetest-rpc- Shared RPC interface and typesconnection-checker- Network connectivity validationsocks-server- SOCKS proxy for testing
End-to-End Tests
End-to-end tests verify complete user workflows and system behavior.Desktop E2E Tests
Desktop e2e tests use Playwright and are written in TypeScript. Test categories:- Mocked tests (
test/e2e/mocked/) - Run with mocked backend APIs - Installed tests (
test/e2e/installed/) - Run against installed application
playwright.config.ts and test/e2e/installed/playwright.config.ts.
Android E2E Tests
Android e2e tests are instrumented tests that run on real devices or emulators. Run Android e2e tests:- Valid Mullvad account number for authentication tests
- Invalid account number for negative test cases
- Access to Mullvad public infrastructure and APIs
/sdcard/Download/test-attachments on the test device.
UI/Graphical Tests
UI tests verify visual and interactive elements of the application.Desktop UI Tests
Desktop UI tests are written in TypeScript using Playwright and located indesktop/packages/mullvad-vpn/test/e2e/.
Key test areas:
- Login flows (
login.spec.ts) - Account expiry (
account-expiry.spec.ts) - Connection states (
main.spec.ts) - Settings screens (
settings.spec.ts) - Location selection (
select-location/) - Notifications (
notifications.spec.ts) - Split tunneling (
split-tunneling.spec.ts)
Test Infrastructure
Test Manager Framework
The test-manager provides a framework for writing integration tests:test-manager/src/tests/:
account.rs- Account management testsconfig.rs- Configuration testsdns.rs- DNS functionality teststunnel.rs- VPN tunnel teststunnel_state.rs- Tunnel state managementaccess_methods.rs- API access method testsaudits/- Security audit verification tests
CI/CD Testing
Tests run automatically in GitHub Actions: Desktop tests:Running All Tests
To run all tests across the project:Test Configuration
Workspace Configuration
The rootCargo.toml defines workspace-wide test dependencies:
Linting and Code Quality
Tests must pass the same linting rules as production code:Best Practices
- Write tests alongside code - Unit tests should be close to the code they test
- Use descriptive test names - Test names should clearly indicate what is being tested
- Test edge cases - Include tests for error conditions and boundary cases
- Keep tests fast - Unit tests should run quickly; save longer tests for integration
- Mock external dependencies - Use mocked backends for faster and more reliable tests
- Use test fixtures - Share common test setup code across tests
- Test public interfaces - Focus on testing public APIs rather than internal implementation