Test de l’interface utilisateur avec Appium

Le test de l’interface utilisateur vérifie que l’interface utilisateur de votre application se comporte correctement en automatisant les interactions telles que l’appui sur les boutons, l’entrée de texte et la navigation entre les pages. Bien que les tests unitaires valident les méthodes et classes individuelles en isolation, les tests de l’interface utilisateur effectuent l’exercice de l’application complète sur un appareil ou un émulateur réel pour intercepter les régressions qui ne s’affichent que par l’interaction utilisateur réelle.

Appium est une infrastructure de test d’interface utilisateur open source qui fonctionne avec des applications web natives, hybrides et web sur plusieurs plateformes. Étant donné qu’Appium fonctionne au niveau de la plateforme, il teste votre application de la même façon qu’elle ait été créée avec des outils .NET MAUI ou natifs de la plateforme. Appium utilise des pilotes spécifiques à la plateforme pour envoyer des interactions à votre application :

Plate-forme Pilote Appium Système d’exploitation hôte
Android UIAutomator2 Windows, macOS, Linux
Ios XCUITest macOS uniquement
Mac Catalyst Mac2 macOS uniquement
Windows Windows Windows uniquement

Note

Ce que vous pouvez tester dépend de votre machine de développement. Sur Windows, vous pouvez tester les applications Android et Windows. Sur macOS, vous pouvez tester les applications Android, iOS et Mac Catalyst.

Conseil / Astuce

Pour obtenir un exemple de travail complet, consultez l’exemple Basic Appium NUnit sur GitHub.

Prerequisites

Avant de pouvoir écrire et exécuter des tests d’interface utilisateur, installez les conditions préalables suivantes :

Node.js et Appium

Appium est basé sur Node.js. Installez Node.js (version LTS recommandée), puis installez Appium et les pilotes pour les plateformes que vous souhaitez tester :

# Install Appium
npm install -g appium

# Install platform drivers (install only the ones you need)
appium driver install uiautomator2     # Android
appium driver install xcuitest         # iOS (macOS only)
appium driver install mac2             # Mac Catalyst (macOS only)
appium driver install windows          # Windows

Vérifiez l’installation en s’exécutant appium dans un terminal. Le serveur doit démarrer et afficher un message indiquant qu’il écoute sur le port 4723.

pilote d’application Windows (Windows uniquement)

Le pilote Appium Windows utilise Windows Application Driver (WinAppDriver) pour automatiser les applications Windows. Téléchargez et installez WinAppDriver version 1.2.1.

Important

Utilisez spécifiquement WinAppDriver version 1.2.1. D’autres versions peuvent ne pas fonctionner correctement avec le pilote Appium Windows.

Android SDK (Android uniquement)

Vérifiez que le Kit de développement logiciel (SDK) Android est installé et que la ANDROID_HOME variable d’environnement pointe vers son emplacement. Si vous avez déjà configuré votre machine pour .NET MAUI développement Android, cela doit être en place.

Préparer l’application .NET MAUI pour le test

Tous les éléments d’interface utilisateur avec lesquels vous souhaitez interagir à partir de vos tests doivent avoir la AutomationId propriété définie sur une valeur unique. Cette propriété est mappée aux identificateurs d’accessibilité spécifiques à la plateforme que Appium utilise pour localiser des éléments.

<Button
    x:Name="CounterBtn"
    AutomationId="CounterBtn"
    Text="Click me"
    SemanticProperties.Hint="Counts the number of times you click"
    Clicked="OnCounterClicked"
    HorizontalOptions="Fill" />

Enregistrement d'activité Android

Pour Android, Appium doit connaître le nom complet de l’activité pour lancer votre application. Ajoutez un [Register] attribut à votre MainActivity classe avec une valeur qui correspond au nom du package de votre application :

[Register("com.companyname.myapp.MainActivity")]
public class MainActivity : MauiAppCompatActivity
{
}

Vérifiez que la valeur de l’attribut Register correspond au ApplicationId fichier de votre projet et à la AppActivity fonctionnalité de votre configuration de test.

Créer les projets de test

Une structure de projet éprouvée utilise des projets de test distincts par plateforme, avec du code de test partagé dans un projet NoTargets. Il s’agit du même modèle utilisé dans l’exemple official et la base de code .NET MAUI elle-même :

MySolution/
├── MauiApp/                  # Your .NET MAUI app
├── UITests.Shared/           # Shared test code (NoTargets project)
├── UITests.Android/          # Android-specific setup
├── UITests.iOS/              # iOS-specific setup
├── UITests.Windows/          # Windows-specific setup
└── UITests.macOS/            # macOS-specific setup

Chaque projet de plateforme compile le code partagé en liant les fichiers du projet partagé. Cela signifie que tous les tests du projet partagé s’exécutent sur chaque plateforme que vous testez. Le projet partagé lui-même ne peut pas être exécuté directement. Exécutez toujours l’un des projets de test spécifiques à la plateforme.

Important

Conservez l’espace de noms identique dans tous les projets de test (par exemple, UITests). L’attribut de NUnit exécute des méthodes de [SetUpFixture] configuration pour tous les appareils de test dans le même espace de noms. Si les espaces de noms ne correspondent pas, le pilote Appium ne sera pas initialisé lors de l’exécution de vos tests.

Packages NuGet du projet de test de plateforme

Chaque projet de test spécifique à la plateforme a besoin des packages NuGet suivants :

<ItemGroup>
    <PackageReference Include="Appium.WebDriver" Version="8.0.0" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
    <PackageReference Include="NUnit" Version="3.14.0" />
    <PackageReference Include="NUnit3TestAdapter" Version="4.5.0" />
</ItemGroup>

Conseil / Astuce

Au lieu de créer des projets de test à partir de zéro, vous pouvez installer des modèles gérés par la communauté qui créent la structure complète du projet pour vous. Pour plus d’informations, consultez le référentiel Template.Maui.UITesting GitHub.

Écrire une classe de test de base

Créez un BaseTest.cs fichier dans le projet partagé. Cette classe de base fournit l’accès au pilote Appium et à une méthode d’assistance qui gère une différence dans la façon dont les éléments se trouvent sur Windows et sur d’autres plateformes :

using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;

namespace UITests;

public abstract class BaseTest
{
    protected AppiumDriver App => AppiumSetup.App;

    protected AppiumElement FindUIElement(string id)
    {
        if (App is WindowsDriver)
        {
            return App.FindElement(MobileBy.AccessibilityId(id));
        }

        return App.FindElement(MobileBy.Id(id));
    }
}

Sur Windows, AutomationId est accessible via MobileBy.AccessibilityId. Sur Android, iOS et macOS, utilisez MobileBy.Id.

Démarrer le serveur Appium

Le serveur Appium doit être en cours d’exécution avant d’exécuter des tests. L’approche la plus simple consiste à le démarrer manuellement à partir d’un terminal :

appium

Par défaut, le serveur démarre sur http://127.0.0.1:4723. Laissez-le tourner pendant que vous faites vos tests.

Démarrez le serveur par programmation (facultatif)

Pour rendre votre suite de tests autonome, vous pouvez démarrer et arrêter le serveur Appium dans le cadre de l’exécution de test. Créez un AppiumServerHelper.cs fichier dans le projet partagé :

using OpenQA.Selenium.Appium.Service;

namespace UITests;

public static class AppiumServerHelper
{
    private static AppiumLocalService? _appiumLocalService;

    public const string DefaultHostAddress = "127.0.0.1";
    public const int DefaultHostPort = 4723;

    public static void StartAppiumLocalServer(
        string host = DefaultHostAddress,
        int port = DefaultHostPort)
    {
        if (_appiumLocalService is not null)
            return;

        var builder = new AppiumServiceBuilder()
            .WithIPAddress(host)
            .UsingPort(port);

        _appiumLocalService = builder.Build();
        _appiumLocalService.Start();
    }

    public static void DisposeAppiumLocalServer()
    {
        _appiumLocalService?.Dispose();
    }
}

Configurer Appium pour chaque plateforme

Chaque projet de plateforme contient un AppiumSetup.cs fichier qui configure le pilote Appium avec des options spécifiques à la plateforme. Cette classe utilise l’attribut de [SetUpFixture] NUnit pour initialiser le pilote une fois avant l’exécution de tous les tests.

Android

using NUnit.Framework;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Enums;

namespace UITests;

[SetUpFixture]
public class AppiumSetup
{
    private static AppiumDriver? driver;

    public static AppiumDriver App => driver
        ?? throw new NullReferenceException("AppiumDriver is null");

    [OneTimeSetUp]
    public void RunBeforeAnyTests()
    {
        AppiumServerHelper.StartAppiumLocalServer();

        var androidOptions = new AppiumOptions
        {
            AutomationName = "UIAutomator2",
            PlatformName = "Android",
        };

        // For debug builds, use NoReset to preserve Fast Deployment libraries
        androidOptions.AddAdditionalAppiumOption(
            MobileCapabilityType.NoReset, "true");
        androidOptions.AddAdditionalAppiumOption(
            AndroidMobileCapabilityType.AppPackage,
            "com.companyname.myapp");
        androidOptions.AddAdditionalAppiumOption(
            AndroidMobileCapabilityType.AppActivity,
            "com.companyname.myapp.MainActivity");

        driver = new AndroidDriver(androidOptions);
    }

    [OneTimeTearDown]
    public void RunAfterAnyTests()
    {
        driver?.Quit();
        AppiumServerHelper.DisposeAppiumLocalServer();
    }
}

Note

Pour les versions de débogage sur Android, définissez NoReset à true. Les versions de débogage utilisent Fast Deployment, et le comportement de réinitialisation par défaut d’Appium supprime les bibliothèques nécessaires à Fast Deployment. Pour les versions finales, vous pouvez définir la propriété App sur le chemin d’accès complet du fichier signé .apk.

Ios

using NUnit.Framework;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.iOS;

namespace UITests;

[SetUpFixture]
public class AppiumSetup
{
    private static AppiumDriver? driver;

    public static AppiumDriver App => driver
        ?? throw new NullReferenceException("AppiumDriver is null");

    [OneTimeSetUp]
    public void RunBeforeAnyTests()
    {
        AppiumServerHelper.StartAppiumLocalServer();

        var iOSOptions = new AppiumOptions
        {
            AutomationName = "XCUITest",
            PlatformName = "iOS",
            PlatformVersion = "17.0",
            DeviceName = "iPhone 15 Pro",
            App = "com.companyname.myapp",
        };

        driver = new IOSDriver(iOSOptions);
    }

    [OneTimeTearDown]
    public void RunAfterAnyTests()
    {
        driver?.Quit();
        AppiumServerHelper.DisposeAppiumLocalServer();
    }
}

Windows

using NUnit.Framework;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;

namespace UITests;

[SetUpFixture]
public class AppiumSetup
{
    private static AppiumDriver? driver;

    public static AppiumDriver App => driver
        ?? throw new NullReferenceException("AppiumDriver is null");

    [OneTimeSetUp]
    public void RunBeforeAnyTests()
    {
        AppiumServerHelper.StartAppiumLocalServer();

        var windowsOptions = new AppiumOptions
        {
            AutomationName = "windows",
            PlatformName = "Windows",
            App = "com.companyname.myapp_9zz4h110yvjzm!App",
        };

        driver = new WindowsDriver(windowsOptions);
    }

    [OneTimeTearDown]
    public void RunAfterAnyTests()
    {
        driver?.Quit();
        AppiumServerHelper.DisposeAppiumLocalServer();
    }
}

Mac Catalyst

using NUnit.Framework;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Enums;
using OpenQA.Selenium.Appium.Mac;

namespace UITests;

[SetUpFixture]
public class AppiumSetup
{
    private static AppiumDriver? driver;

    public static AppiumDriver App => driver
        ?? throw new NullReferenceException("AppiumDriver is null");

    [OneTimeSetUp]
    public void RunBeforeAnyTests()
    {
        AppiumServerHelper.StartAppiumLocalServer();

        var macOptions = new AppiumOptions
        {
            AutomationName = "mac2",
            PlatformName = "Mac",
            App = "/path/to/MyApp/bin/Debug/net10.0-maccatalyst/maccatalyst-x64/MyApp.app",
        };

        macOptions.AddAdditionalAppiumOption(
            IOSMobileCapabilityType.BundleId,
            "com.companyname.myapp");

        driver = new MacDriver(macOptions);
    }

    [OneTimeTearDown]
    public void RunAfterAnyTests()
    {
        driver?.Quit();
        AppiumServerHelper.DisposeAppiumLocalServer();
    }
}

Important

Pour Mac Catalyst, vous devez définir l’option BundleId . Sans cela, Appium automatise Finder au lieu de votre application.

Écrire des tests d’interface utilisateur

Ajoutez des classes de test au projet Partagé afin qu’elles s’exécutent sur toutes les plateformes. Chaque classe de test doit hériter de BaseTest. L’exemple suivant teste le bouton compteur dans le modèle .NET MAUI par défaut :

using NUnit.Framework;

namespace UITests;

public class MainPageTests : BaseTest
{
    [Test]
    public void AppLaunches()
    {
        App.GetScreenshot().SaveAsFile($"{nameof(AppLaunches)}.png");
    }

    [Test]
    public void ClickCounterTest()
    {
        // Arrange
        var element = FindUIElement("CounterBtn");

        // Act
        element.Click();
        Task.Delay(500).Wait();

        // Assert
        App.GetScreenshot().SaveAsFile($"{nameof(ClickCounterTest)}.png");
        Assert.That(element.Text, Is.EqualTo("Clicked 1 time"));
    }
}

La FindUIElement méthode localise les contrôles par leur AutomationId valeur. Vous pouvez également utiliser d’autres sélecteurs :

Selector Méthode Remarques
AutomationId MobileBy.Id() ou MobileBy.AccessibilityId() Préféré. Utilisez l'assistant FindUIElement pour abstraire la différence entre les plateformes.
XPath MobileBy.XPath() Plus lent, mais utile pour les requêtes complexes.
Nom de classe MobileBy.ClassName() Recherche des éléments par leur nom de classe natif.

Conseil / Astuce

Définissez AutomationId sur chaque élément que vous souhaitez tester. Il s’agit du moyen le plus fiable et performant de localiser des éléments sur toutes les plateformes.

Exécuter des tests d’interface utilisateur

Avant d’exécuter des tests, assurez-vous que :

  • L’application .NET MAUI est déployée sur l’appareil ou l’émulateur cible.
  • Pour Android, un émulateur est démarré ou un appareil physique est connecté.
  • Pour iOS, un simulateur est en cours d’exécution ou un appareil est approvisionné.
  • Pour Windows, l’application est installée (par exemple, en l’exécutant d’abord à partir de Visual Studio).
  • Pour macOS, le .app bundle est généré et disponible sur le chemin spécifié dans AppiumSetup.cs.

Exécutez des tests à partir de Visual Studio à l’aide de Test Explorer ou à partir de la ligne de commande :

dotnet test UITests.Android/UITests.Android.csproj

Remplacez le chemin du projet par la plateforme que vous souhaitez tester.

Voir également