Build and run .NET MAUI device tests locally with category filtering. Supports iOS, MacCatalyst, Android on macOS; Android, Windows on Windows. Use TestFilter to run specific test categories.
Build and run .NET MAUI device tests locally on iOS simulators, MacCatalyst, Android emulators, or Windows.
| Host OS | Supported Platforms |
|---|---|
| macOS | ios, maccatalyst, android |
| Windows | android, windows |
This skill uses bash together with pwsh (PowerShell 7+) to run the PowerShell scripts. Requires:
xharness - Global dotnet tool for running tests on iOS/MacCatalyst/Androiddotnet - .NET SDK with platform workloads installedThis skill uses shared infrastructure scripts:
.github/scripts/shared/Start-Emulator.ps1 - Detects and boots iOS simulators / Android emulators.github/scripts/shared/shared-utils.ps1 - Common utility functionsThese are automatically loaded by the Run-DeviceTests.ps1 script.
| Project | Path |
|---|---|
| Controls | src/Controls/tests/DeviceTests/Controls.DeviceTests.csproj |
| Core | src/Core/tests/DeviceTests/Core.DeviceTests.csproj |
| Essentials | src/Essentials/test/DeviceTests/Essentials.DeviceTests.csproj |
| Graphics | src/Graphics/tests/DeviceTests/Graphics.DeviceTests.csproj |
| BlazorWebView | src/BlazorWebView/tests/DeviceTests/MauiBlazorWebView.DeviceTests.csproj |
| AI | src/AI/tests/Essentials.AI.DeviceTests/Essentials.AI.DeviceTests.csproj |
All scripts are in .github/skills/run-device-tests/scripts/
# Run Controls device tests on iOS simulator (default on macOS)
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios
# Run Core device tests on MacCatalyst
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform maccatalyst
# Run Controls device tests on Android emulator
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform android
# Run Controls device tests on Windows (default on Windows)
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform windows
# Run on specific iOS version
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform ios -iOSVersion 26
# Run with test filter
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Button"
# Run other test projects
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Essentials -Platform android
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Graphics -Platform maccatalyst
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project BlazorWebView -Platform ios
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -BuildOnly
# iOS simulators
xcrun simctl list devices available
# Android emulators
emulator -list-avds
scripts/Run-DeviceTests.ps1 -Project <name> -Platform <platform> automatically detects/boots device, builds, and runs testsartifacts/log/ directory for detailed test resultsartifacts/bin/<Project>.DeviceTests/<Configuration>/<tfm>/<rid>/artifacts/log/xharness global tool: dotnet tool install --global Microsoft.DotNet.XHarness.CLI# Quick test run for Controls on iOS (default on macOS)
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios
# Test on MacCatalyst
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform maccatalyst
# Test on Android emulator (works on both macOS and Windows)
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform android
# Test on Windows (default on Windows)
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform windows
# Test on iOS 26 specifically
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -iOSVersion 26
# Run only Button category tests on iOS
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Button"
# Build for Android without running tests
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Core -Platform android -BuildOnly
.github/scripts/shared/Start-Emulator.ps1The -TestFilter parameter allows running specific test categories instead of all tests. This is useful for quick iteration during development.
| Format | Description | Example |
|---|---|---|
Category=X | Run only category X | Category=Button |
SkipCategories=X,Y,Z | Skip categories X, Y, Z | SkipCategories=Shell,CollectionView |
# Run only Button tests on iOS
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "Category=Button"
# Run only Button tests on Android
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform android -TestFilter "Category=Button"
# Skip heavy test categories
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -TestFilter "SkipCategories=Shell,CollectionView,HybridWebView"
Test filtering is implemented in src/Core/tests/DeviceTests.Shared/DeviceTestSharedHelpers.cs:
| Platform | How Filter is Passed | How Filter is Read |
|---|---|---|
| iOS/MacCatalyst | --set-env=TestFilter=... | NSProcessInfo.ProcessInfo.Environment["TestFilter"] |
| Android | --arg TestFilter=... | MauiTestInstrumentation.Current.Arguments.GetString("TestFilter") |
| Windows | --filter "Category=..." | Native vstest filter |
Common categories in Controls.DeviceTests:
Button, Label, Entry, Editor - Individual control testsCollectionView, ListView, CarouselView - Collection controls (heavy)Shell, Navigation, TabbedPage - Navigation tests (heavy)Layout, FlexLayout - Layout testsMemory - Memory leak testsAccessibility - Accessibility testsGesture - Gesture recognition testsTo see all categories, check src/Controls/tests/DeviceTests/TestCategory.cs.
If you see errors about Xcode version mismatch (e.g., "Xcode 26.2 installed but 26.0 required"):
# Use -SkipXcodeVersionCheck to bypass validation
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform ios -SkipXcodeVersionCheck
MacCatalyst apps use display names (e.g., "Controls Tests.app") not assembly names. The script handles this automatically by searching for .app bundles in the output directory.
Android package names must be lowercase (e.g., com.microsoft.maui.controls.devicetests). The script has a mapping of project names to correct package names.
Ensure full rebuild: The test filter code must be compiled into the app. Delete artifacts and rebuild:
rm -rf artifacts/bin/<Project>.DeviceTests
pwsh .github/skills/run-device-tests/scripts/Run-DeviceTests.ps1 -Project Controls -Platform android -TestFilter "Category=Button"
Check the console output: Look for TestFilter: Category=Button in the test output to confirm filter was applied.
Verify category exists: Ensure the category name matches exactly (case-sensitive).
The script automatically handles XHarness device targeting for iOS and Android:
xcrun simctl to get iOS version from booted simulator--target ios-simulator-64_VERSION and --device UDID to xharness for explicit targeting--target maccatalystemulator-5554)--device-id to xharness android commanddotnet test) for test executionWhy both --target and --device for iOS?
--target ios-simulator-64 (or ios-simulator-64_VERSION) to specify platform type--device UDID explicitly tells xharness which simulator to useExample xharness invocations:
# iOS with test filter
dotnet xharness apple test \
--app path/to/app \
--target ios-simulator-64_18.5 \
--device 56AE278D-60F7-4892-9DE0-6341357CA068 \
-o artifacts/log \
--timeout 01:00:00 \
-v \
--set-env=TestFilter=Category=Button
# MacCatalyst with test filter
dotnet xharness apple test \
--app path/to/app \
--target maccatalyst \
-o artifacts/log \
--timeout 01:00:00 \
-v \
--set-env=TestFilter=Category=Button
# Android with test filter
dotnet xharness android test \
--app path/to/app.apk \
--package-name com.microsoft.maui.controls.devicetests \
--device-id emulator-5554 \
-o artifacts/log \
--timeout 01:00:00 \
-v \
--arg TestFilter=Category=Button
This ensures tests run on the correct device with proper version targeting and filtering.