Token导航 LogoToken导航TokenDH.com
研究检索敏感数据github未标认证来源可访问许可证需确认审计异常

dotnet-maui-testingdotnet 毛伊岛测试

Agent Skill

用于辅助测试设计、自动化测试、用例整理和回归验证。它适合让 Agent 编写单元测试、端到端测试、测试计划或根据失败日志定位问题。使用时需要确认项目测试框架、运行命令和夹具数据,避免为了通过测试而改坏真实逻辑;涉及浏览器或外部服务时,应区分本地模拟、测试环境和生产环境。

总安装

428

周安装

18

GitHub Stars

15

下载量

150
CodexClaudeCursorGemini CLI

安装说明

本站只整理中文说明和来源信息,不托管安装包,也不代用户安装。

GitHub

来源数

2

许可证

unknown

最后核验

2026-05-01

来源状态

来源可访问

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:dotnet-maui-testing(dotnet 毛伊岛测试)
来源仓库:https://github.com/wshaddix/dotnet-skills
仓库路径:skills/dotnet-maui-testing
安装命令:
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-maui-testing
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/wshaddix/dotnet-skills --skill dotnet-maui-testing

简介

用于辅助测试设计、自动化测试和回归验证。

  • 适合编写 UI 自动化测试或分析失败日志。
  • 使用时需确认测试框架与设备连接状态。适用宿主包括 Codex、Claude、Cursor、Gemini CLI,接入前应确认版本、权限和运行环境要求。
  • 涉及移动端时应区分模拟器与真机环境。
  • 注意:不覆盖浏览器测试等边界内容。dotnet-maui-testing 属于研究检索类 Skill,可作为该场景下的辅助能力补充。

SKILL.md

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+).

Out of scope: Shared UI testing patterns (page object model, wait strategies) are in [skill:dotnet-ui-testing-core]. Browser-based testing is covered by [skill:dotnet-playwright]. Test project scaffolding is owned by [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" />

Driver Initialization

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;
    }
}

Test Configuration

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, "..", "..", "..", ".."));
}

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

<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>

Finding Elements in Tests

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();
        passwordField.SendKeys("P@ssw0rd!");
        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);
    }
}

Page Object Model for MAUI

Apply the page object model pattern (see [skill:dotnet-ui-testing-core]) with Appium's driver:

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);
    var homePage = loginPage.Login("alice", "P@ssw0rd!");

    Assert.True(homePage.IsLoaded);
}

Platform-Specific Behavior Testing

Conditional Tests by Platform

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 returned to the list
        var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(5));
        wait.Until(d => d.FindElement(MobileBy.AccessibilityId("item-list")));
    }

    [Fact]
    [Trait("Platform", "iOS")]
    public void SwipeToDelete_iOS_RemovesItem()
    {
        // xUnit v3 native skip support
        Assert.SkipWhen(TestConfig.TargetPlatform != "iOS",
            "iOS-only: swipe gesture");

        var item = _driver.FindElement(MobileBy.AccessibilityId("item-1"));

        // Swipe left to reveal delete action
        var swipe = new PointerInputDevice(PointerKind.Touch, "finger");
        var sequence = new ActionSequence(swipe);
        var itemLocation = item.Location;
        var itemSize = item.Size;

        sequence.AddAction(swipe.CreatePointerMove(
            item, itemSize.Width - 10, itemSize.Height / 2,
            TimeSpan.FromMilliseconds(0)));
        sequence.AddAction(swipe.CreatePointerDown(MouseButton.Left));
        sequence.AddAction(swipe.CreatePointerMove(
            item, 10, itemSize.Height / 2,
            TimeSpan.FromMilliseconds(300)));
        sequence.AddAction(swipe.CreatePointerUp(MouseButton.Left));

        _driver.PerformActions([sequence]);

        // Tap the delete button
        var deleteBtn = _driver.FindElement(MobileBy.AccessibilityId("delete-action"));
        deleteBtn.Click();

        // Verify item removed
        var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(5));
        wait.Until(d =>
        {
            var items = d.FindElements(MobileBy.AccessibilityId("item-1"));
            return items.Count == 0;
        });
    }
}

Screen Size and Orientation

[Fact]
public void Dashboard_LandscapeMode_ShowsSidePanel()
{
    // Rotate to landscape
    _driver.Orientation = ScreenOrientation.Landscape;

    try
    {
        var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(5));
        var sidePanel = wait.Until(d =>
            d.FindElement(MobileBy.AccessibilityId("side-panel")));

        Assert.True(sidePanel.Displayed);
    }
    finally
    {
        // Restore portrait
        _driver.Orientation = ScreenOrientation.Portrait;
    }
}

XHarness Test Execution

XHarness is a command-line tool for running tests on devices and emulators across platforms. It handles app installation, test execution, and result collection.

Running Tests with XHarness

# Install XHarness
dotnet tool install --global Microsoft.DotNet.XHarness.CLI

# Run on Android emulator
xharness android test \
    --app bin/Release/net8.0-android/com.myapp-Signed.apk \
    --package-name com.myapp \
    --instrumentation devicerunner.AndroidInstrumentation \
    --output-directory test-results/android

# Run on iOS simulator
xharness apple test \
    --app bin/Release/net8.0-ios/MyApp.app \
    --target ios-simulator-64 \
    --output-directory test-results/ios

# Run with specific device
xharness android test \
    --app app.apk \
    --package-name com.myapp \
    --device-id emulator-5554 \
    --output-directory test-results

XHarness with Device Runner

For xUnit tests running directly on device, add the device runner NuGet package:

<PackageReference Include="Microsoft.DotNet.XHarness.TestRunners.Xunit" Version="1.*" />
// In the MAUI test app's MauiProgram.cs
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.UseVisualRunner(); // XHarness visual test runner
    return builder.Build();
}

Key Principles

  • Use AutomationId for all testable elements. It is the cross-platform equivalent of data-testid and maps to the native accessibility identifier on every platform.
  • Run tests against real emulators/simulators, not just unit tests. MAUI rendering, navigation, and platform services behave differently than in-memory tests.
  • Use explicit waits, never implicit waits or delays. WebDriverWait with a condition is reliable; Thread.Sleep and implicit waits hide timing issues.
  • Tag platform-specific tests with [Trait] and Assert.SkipWhen. xUnit v3's native skip support allows running the correct tests per platform in CI without failures from unsupported features.
  • Apply the page object model for maintainability. MAUI apps have complex navigation flows; page objects keep tests readable as the app grows.

Agent Gotchas

  1. Do not use FindElement without a wait strategy. Elements may not be available immediately after navigation. Always use WebDriverWait for elements that appear after async operations or page transitions.
  2. Do not hardcode emulator/simulator names. Use environment variables or test configuration so CI can specify the available device. Different CI environments have different emulators installed.
  3. Do not forget to set AutomationId on MAUI controls. Without it, Appium falls back to platform-specific selectors (XPath, class name) that differ across Android, iOS, and Windows -- breaking cross-platform tests.
  4. Do not run iOS tests on non-macOS machines. iOS simulators require Xcode, which is macOS-only. Use platform-conditional test skipping or separate CI pipelines per platform.
  5. Do not leave the Appium server unmanaged. Start Appium as a fixture or CI service, not manually. Forgotten Appium processes cause port conflicts and test hangs.

References

适合场景

01

用户想查找某类 Agent Skill 时

02

需要根据任务场景推荐可安装能力包时

03

需要对比不同来源的安装命令和来源信息时

能力概览

能力 1

按任务关键词查找相关 Skills

能力 2

展示可复制的安装命令

能力 3

保留来源站点、仓库和原始说明,方便继续核验

能力 4

展示第三方安全扫描或审计结果

安装后应在对应宿主中按原始 README 的触发条件使用;具体调用方式请以来源页面和 README 为准。

平台分布

Codex

35.6%
按下载量换算53

Claude

31.98%
按下载量换算48

Cursor

18.92%
按下载量换算28

Gemini CLI

9.79%
按下载量换算15

安全审计

Gen Agent Trust Hub

通过

Socket

未通过

Snyk

通过

权限和风险

敏感数据

该 Skill 可能接触密钥、Token、环境变量或敏感配置,应进入高风险复核队列,默认不自动发布。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。来源安全扫描存在 warning/failed 结果,不能写成本站确认安全。当前只有一个来源,正式发布前建议补源仓库或其他目录站核验。

来源信息

继续浏览同类 Skills