dotnet-maui-testing
Automated UI testing for .NET MAUI apps using Appium and XHarness.
Install
mkdir -p .claude/skills/dotnet-maui-testing && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/15812" && unzip -o skill.zip -d .claude/skills/dotnet-maui-testing && rm skill.zipInstalls to .claude/skills/dotnet-maui-testing
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Tests .NET MAUI apps. Appium device automation, XHarness, platform validation.Key capabilities
- →Automate UI testing for Android, iOS, and Windows MAUI apps
- →Execute cross-platform tests using XHarness
- →Locate elements using MAUI's AutomationId
- →Configure Appium drivers for different platforms
- →Manage test configuration for device and emulator testing
How it works
The skill uses Appium 2.x for UI automation and XHarness for cross-platform test execution, use MAUI's AutomationId for reliable element location.
Inputs & outputs
When to use dotnet-maui-testing
- →Setup Appium for MAUI
- →Validate platform-specific behavior
- →Execute cross-platform tests
- →Configure emulator testing
About this skill
dotnet-maui-testing
Testing .NET MAUI applications using Appium for UI automation and XHarness for cross-platform test execution. Covers device and emulator testing, platform-specific behavior validation, element location strategies for MAUI controls, and test infrastructure for mobile/desktop apps.
Version assumptions: .NET 8.0+ baseline, Appium 2.x with UIAutomator2 (Android) and XCUITest (iOS) drivers, XHarness 1.x. Examples use the latest Appium .NET client (5.x+).
Scope
- Appium 2.x UI automation for Android, iOS, and Windows
- XHarness cross-platform test execution
- Platform-specific behavior validation
- Element location strategies for MAUI controls
- Test infrastructure for mobile/desktop apps
Out of scope
- Shared UI testing patterns (page object model, wait strategies) -- see [skill:dotnet-ui-testing-core]
- Browser-based testing -- see [skill:dotnet-playwright]
- Test project scaffolding -- see [skill:dotnet-add-testing]
Prerequisites: MAUI test project scaffolded via [skill:dotnet-add-testing]. Appium server installed
(npm install -g appium). For Android: Android SDK with emulator configured. For iOS: Xcode with simulator (macOS
only). For Windows: WinAppDriver installed.
Cross-references: [skill:dotnet-ui-testing-core] for page object model, test selectors, and async wait patterns, [skill:dotnet-xunit] for xUnit fixtures and test organization, [skill:dotnet-maui-development] for MAUI project structure, XAML/MVVM patterns, and platform services, [skill:dotnet-maui-aot] for Native AOT on iOS/Mac Catalyst and AOT build testing considerations.
Appium Setup for MAUI
Packages
<PackageReference Include="Appium.WebDriver" Version="5.*" />
<PackageReference Include="xunit.v3" Version="3.2.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" />
```xml
### Driver Initialization
```csharp
public class AppiumFixture : IAsyncLifetime
{
public AppiumDriver Driver { get; private set; } = null!;
public ValueTask InitializeAsync()
{
var options = new AppiumOptions();
if (OperatingSystem.IsAndroid() || TestConfig.TargetPlatform == "Android")
{
options.PlatformName = "Android";
options.AutomationName = "UiAutomator2";
options.App = TestConfig.AndroidApkPath;
options.AddAdditionalAppiumOption("deviceName", "Pixel_7_API_34");
options.AddAdditionalAppiumOption("avd", "Pixel_7_API_34");
}
else if (OperatingSystem.IsIOS() || TestConfig.TargetPlatform == "iOS")
{
options.PlatformName = "iOS";
options.AutomationName = "XCUITest";
options.App = TestConfig.iOSAppPath;
options.AddAdditionalAppiumOption("deviceName", "iPhone 15");
options.AddAdditionalAppiumOption("platformVersion", "17.2");
}
else if (OperatingSystem.IsWindows() || TestConfig.TargetPlatform == "Windows")
{
options.PlatformName = "Windows";
options.AutomationName = "Windows";
options.App = TestConfig.WindowsAppPath;
}
Driver = new AppiumDriver(
new Uri("http://localhost:4723"), options);
// Explicit waits only -- do not set ImplicitWait (it causes
// additive timeout behavior when combined with WebDriverWait)
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.Zero;
return ValueTask.CompletedTask;
}
public ValueTask DisposeAsync()
{
Driver?.Quit();
return ValueTask.CompletedTask;
}
}
```text
### Test Configuration
```csharp
public static class TestConfig
{
// Set via environment variables or test runsettings
public static string TargetPlatform =>
Environment.GetEnvironmentVariable("TEST_PLATFORM") ?? "Android";
public static string AndroidApkPath =>
Environment.GetEnvironmentVariable("ANDROID_APK_PATH")
?? Path.Combine(SolutionDir, "bin", "Release", "net8.0-android", "com.myapp-Signed.apk");
public static string iOSAppPath =>
Environment.GetEnvironmentVariable("IOS_APP_PATH")
?? Path.Combine(SolutionDir, "bin", "Release", "net8.0-ios", "MyApp.app");
public static string WindowsAppPath =>
Environment.GetEnvironmentVariable("WINDOWS_APP_PATH")
?? "com.mycompany.myapp_1.0.0.0_x64__9a0dh7ch11qe4!App";
private static string SolutionDir =>
Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", ".."));
}
```text
---
## Element Location with AutomationId
MAUI's `AutomationId` property maps to the platform-native accessibility identifier. This is the most reliable selector
for cross-platform tests.
### Setting AutomationId in XAML
```xml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<VerticalStackLayout>
<Entry AutomationId="username-input"
Placeholder="Username" />
<Entry AutomationId="password-input"
Placeholder="Password"
IsPassword="True" />
<Button AutomationId="login-button"
Text="Log In"
Clicked="OnLoginClicked" />
<Label AutomationId="error-message"
TextColor="Red" />
</VerticalStackLayout>
</ContentPage>
```text
### Finding Elements in Tests
```csharp
public class LoginTests : IClassFixture<AppiumFixture>
{
private readonly AppiumDriver _driver;
public LoginTests(AppiumFixture fixture)
{
_driver = fixture.Driver;
}
[Fact]
public void Login_ValidCredentials_NavigatesToHome()
{
// Find by AutomationId (maps to accessibility ID on each platform)
var usernameField = _driver.FindElement(MobileBy.AccessibilityId("username-input"));
var passwordField = _driver.FindElement(MobileBy.AccessibilityId("password-input"));
var loginButton = _driver.FindElement(MobileBy.AccessibilityId("login-button"));
usernameField.Clear();
usernameField.SendKeys("testuser");
passwordField.Clear();
// Use placeholder password in examples
passwordField.SendKeys("<TEST_PASSWORD_PLACEHOLDER>");
loginButton.Click();
// Wait for navigation
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(10));
var homeTitle = wait.Until(d =>
d.FindElement(MobileBy.AccessibilityId("home-title")));
Assert.Equal("Welcome", homeTitle.Text);
}
[Fact]
public void Login_InvalidCredentials_ShowsError()
{
var usernameField = _driver.FindElement(MobileBy.AccessibilityId("username-input"));
var passwordField = _driver.FindElement(MobileBy.AccessibilityId("password-input"));
var loginButton = _driver.FindElement(MobileBy.AccessibilityId("login-button"));
usernameField.Clear();
usernameField.SendKeys("wrong");
passwordField.Clear();
passwordField.SendKeys("wrong");
loginButton.Click();
var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(5));
var errorLabel = wait.Until(d =>
d.FindElement(MobileBy.AccessibilityId("error-message")));
Assert.Contains("Invalid", errorLabel.Text);
}
}
```text
---
## Page Object Model for MAUI
Apply the page object model pattern (see [skill:dotnet-ui-testing-core]) with Appium's driver:
```csharp
public class LoginPage
{
private readonly AppiumDriver _driver;
private readonly WebDriverWait _wait;
public LoginPage(AppiumDriver driver)
{
_driver = driver;
_wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
WaitForPageLoaded();
}
private AppiumElement UsernameField =>
_driver.FindElement(MobileBy.AccessibilityId("username-input"));
private AppiumElement PasswordField =>
_driver.FindElement(MobileBy.AccessibilityId("password-input"));
private AppiumElement LoginButton =>
_driver.FindElement(MobileBy.AccessibilityId("login-button"));
private AppiumElement ErrorMessage =>
_driver.FindElement(MobileBy.AccessibilityId("error-message"));
public HomePage Login(string username, string password)
{
UsernameField.Clear();
UsernameField.SendKeys(username);
PasswordField.Clear();
PasswordField.SendKeys(password);
LoginButton.Click();
return new HomePage(_driver);
}
public string GetErrorText()
{
_wait.Until(d =>
{
var el = d.FindElement(MobileBy.AccessibilityId("error-message"));
return !string.IsNullOrEmpty(el.Text);
});
return ErrorMessage.Text;
}
private void WaitForPageLoaded()
{
_wait.Until(d => d.FindElement(MobileBy.AccessibilityId("login-button")));
}
}
// Usage
[Fact]
public void Login_ValidUser_ReachesHomePage()
{
var loginPage = new LoginPage(_driver);
// Use placeholder password in examples
var homePage = loginPage.Login("alice", "<TEST_PASSWORD_PLACEHOLDER>");
Assert.True(homePage.IsLoaded);
}
```text
---
## Platform-Specific Behavior Testing
### Conditional Tests by Platform
```csharp
public class PlatformTests : IClassFixture<AppiumFixture>
{
private readonly AppiumDriver _driver;
public PlatformTests(AppiumFixture fixture)
{
_driver = fixture.Driver;
}
[Fact]
[Trait("Platform", "Android")]
public void BackButton_Android_NavigatesBack()
{
// xUnit v3 native skip support (no SkippableFact package needed)
Assert.SkipWhen(TestConfig.TargetPlatform != "Android",
"Android-only: hardware back button");
// Navigate to details page
_driver.FindElement(MobileBy.AccessibilityId("item-1")).Click();
// Press Android back button
_driver.Navigate().Back();
// Verify we
---
*Content truncated.*
When not to use it
- →When testing shared UI patterns (page object model, wait strategies)
- →When performing browser-based testing
- →When scaffolding a test project
Prerequisites
Limitations
- →It assumes .NET 8.0+ baseline
- →It requires Appium 2.x with UIAutomator2 (Android) and XCUITest (iOS) drivers
- →It requires XHarness 1.x
How it compares
This skill provides specific guidance and setup for testing .NET MAUI applications across multiple platforms using Appium and XHarness, offering a tailored solution for MAUI's unique cross-platform nature.
Compared to similar skills
dotnet-maui-testing side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| dotnet-maui-testing (this skill) | 0 | 5mo | Review | Intermediate |
| run-device-tests | 3 | 2mo | Review | Advanced |
| csharp-pro | 9 | 4mo | No flags | Intermediate |
| performance-benchmark | 3 | 4mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by rudironsoni
View all by rudironsoni →You might also like
run-device-tests
dotnet
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.
csharp-pro
sickn33
Write modern C# code with advanced features like records, pattern matching, and async/await. Optimizes .NET applications, implements enterprise patterns, and ensures comprehensive testing. Use PROACTIVELY for C# refactoring, performance optimization, or complex .NET solutions.
performance-benchmark
dotnet
Generate and run ad hoc performance benchmarks to validate code changes. Use this when asked to benchmark, profile, or validate the performance impact of a code change in dotnet/runtime.
backend-testing
exceptionless
Backend testing with xUnit, Foundatio.Xunit, integration tests with AppWebHostFactory, FluentClient, ProxyTimeProvider for time manipulation, and test data builders. Keywords: xUnit, Fact, Theory, integration tests, AppWebHostFactory, FluentClient, ProxyTimeProvider, TimeProvider, Foundatio.Xunit, TestWithLoggingBase, test data builders
bunit-test-migration
FritzAndFriends
Migrate bUnit test files from deprecated beta API (1.0.0-beta-10) to bUnit 2.x stable API. Use this when working on .razor test files in BlazorWebFormsComponents.Test that contain old patterns like TestComponentBase, Fixture, or SnapshotTest.
deployment-e2e-testing
dotnet
Guide for writing Aspire deployment end-to-end tests. Use this when asked to create, modify, or debug deployment E2E tests that deploy to Azure.