Testes em .NET MAUI: Guia Completo para Unit, UI e Integração em 2026
Guia prático de testes em .NET MAUI em 2026: pirâmide de testes unitários (xUnit/NUnit), integração com DeviceTests e UI com Appium 2, incluindo CI/CD no GitHub Actions e cobertura com Coverlet.
Testar aplicações .NET MAUI em 2026 envolve combinar três camadas: testes unitários em xUnit ou NUnit, testes de integração via Microsoft.Maui.TestUtils.DeviceTests e testes end-to-end de UI com Appium 2 (ou o novo Microsoft.Maui.Automation), rodando tudo automaticamente em pipelines CI sobre emuladores Android e simuladores iOS. Essa abordagem em pirâmide substitui o antigo Xamarin.UITest, que foi descontinuado em 2023, e garante cobertura confiável em Android, iOS, macOS e Windows com um único código-base.
Adote a pirâmide de testes: 70% unitários (xUnit/NUnit), 20% integração (DeviceTests) e 10% UI end-to-end (Appium 2).
Use CommunityToolkit.Mvvm para tornar ViewModels independentes da UI e 100% testáveis sem renderizar uma página MAUI.
O Xamarin.UITest foi descontinuado. Em 2026, Appium 2 com o driver UiAutomator2/XCUITest é o caminho oficial para UI testing multi-plataforma.
Microsoft.Maui.TestUtils.DeviceTests permite executar testes diretamente em um dispositivo real, validando handlers nativos sem mock.
Integre os três níveis em GitHub Actions ou Azure Pipelines usando dotnet test, xharness e Appium Server como serviço.
Meça cobertura com Coverlet e gere relatórios HTML com ReportGenerator. Mire em ≥70% para ViewModels e Services.
A pirâmide de testes em apps móveis modernos
A pirâmide de testes proposta originalmente por Mike Cohn ainda é, na minha experiência, o modelo mental mais saudável para apps .NET MAUI em 2026. A base larga consiste em testes unitários rápidos e isolados que rodam em milissegundos no runtime .NET puro. Sem dispositivo, sem emulador, sem renderização. O meio cobre testes de integração que envolvem o sistema de handlers da MAUI e validam que componentes nativos respondem corretamente em cada plataforma. E o topo, mais estreito e caro, contém testes end-to-end que simulam interações reais do usuário, com Appium dirigindo o app rodando em Android ou iOS.
Honestamente, inverter essa pirâmide (escrever majoritariamente testes de UI porque "garantem que o app funciona") é o erro mais comum que vejo em equipes saindo do Xamarin.Forms. Testes de UI são frágeis, lentos (10 a 60 segundos cada) e flaky por natureza: animações, latência de emulador e sincronização causam falsos negativos. Para um app comercial típico com 200 ViewModels, mire em algo como 800 a 1.500 testes unitários, 150 a 300 de integração e apenas 30 a 80 de UI cobrindo fluxos críticos (login, checkout, onboarding).
Testes unitários de ViewModels e Services
Testes unitários no .NET MAUI funcionam exatamente como em qualquer projeto .NET 9. Você cria um projeto xunit ou nunit separado, referencia o projeto de domínio (não o projeto MAUI inteiro) e testa classes puras sem dependência de Microsoft.Maui.*. A chave para isso? Ter ViewModels desacoplados. Usar o padrão MVVM com CommunityToolkit e source generators torna cada ViewModel uma classe POCO testável sem precisar instanciar uma página.
Um teste típico de ViewModel valida que comandos disparam mudanças de estado corretamente. Considere um LoginViewModel que chama um IAuthService:
using FluentAssertions;
using Moq;
using Xunit;
public class LoginViewModelTests
{
[Fact]
public async Task SignInCommand_WithValidCredentials_NavigatesToHome()
{
// Arrange
var authService = new Mock<IAuthService>();
var navigation = new Mock<INavigationService>();
authService.Setup(a => a.SignInAsync("[email protected]", "pass123"))
.ReturnsAsync(new AuthResult(Success: true, Token: "jwt"));
var vm = new LoginViewModel(authService.Object, navigation.Object)
{
Email = "[email protected]",
Password = "pass123"
};
// Act
await vm.SignInCommand.ExecuteAsync(null);
// Assert
vm.IsBusy.Should().BeFalse();
vm.ErrorMessage.Should().BeNull();
navigation.Verify(n => n.GoToAsync("//home"), Times.Once);
}
}
Repare que esse teste roda em ~5ms, sem inicializar o runtime MAUI. O segredo é separar o projeto MyApp.Core (classes puras: models, services, ViewModels) do projeto MyApp (que contém Pages, App.xaml e o MauiProgram.cs). Essa separação também melhora tempos de build e habilita reuso futuro em Blazor ou WinUI, caso o produto cresça nessa direção.
Testes de integração com DeviceTests
Testes unitários cobrem lógica, mas não validam se um Button realmente renderiza com a cor correta no iOS, ou se um Entry aceita input em Android. Para isso, a equipe do .NET MAUI mantém o pacote Microsoft.Maui.TestUtils.DeviceTests, a mesma infraestrutura que o próprio repositório dotnet/maui usa para validar regressões em PRs. Vale a pena dar uma olhada no repositório oficial dotnet/maui no GitHub para a implementação de referência.
Um projeto de DeviceTests é, basicamente, um app MAUI completo cujo único trabalho é hospedar e executar testes xUnit em um dispositivo real. Crie-o assim:
Agora dá para escrever testes que instanciam handlers reais:
public class ButtonHandlerTests : HandlerTestBase
{
[Fact, Category(TestCategory.Button)]
public async Task BackgroundColorAppliesToNativeControl()
{
var button = new Button { BackgroundColor = Colors.Crimson };
await InvokeOnMainThreadAsync(() =>
{
var handler = CreateHandler<ButtonHandler>(button);
#if ANDROID
var native = handler.PlatformView;
Assert.Equal(Android.Graphics.Color.Crimson.ToArgb(),
((Android.Graphics.Drawables.ColorDrawable)native.Background).Color);
#elif IOS
var native = handler.PlatformView;
Assert.Equal(UIColor.FromRGB(220, 20, 60), native.BackgroundColor);
#endif
});
}
}
Testes de UI com Appium 2
Para validar fluxos completos do usuário (abrir o app, fazer login, navegar até o checkout) o padrão da indústria em 2026 é Appium 2. Diferente do Appium 1.x, a versão 2 separa drivers (UiAutomator2 para Android, XCUITest para iOS) em pacotes independentes que você instala sob demanda. Combine isso com Appium.WebDriver (o cliente .NET) e dá para escrever testes em C# diretamente:
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Android;
using OpenQA.Selenium.Appium.Enums;
public class LoginUITests : IAsyncLifetime
{
private AndroidDriver _driver = null!;
public Task InitializeAsync()
{
var options = new AppiumOptions
{
PlatformName = "Android",
AutomationName = "UiAutomator2",
DeviceName = "Pixel_7_API_34",
App = "/builds/myapp-uitest.apk"
};
_driver = new AndroidDriver(new Uri("http://127.0.0.1:4723"), options);
return Task.CompletedTask;
}
[Fact]
public void UserCanLogInWithValidCredentials()
{
var email = _driver.FindElement(MobileBy.AccessibilityId("EmailEntry"));
email.SendKeys("[email protected]");
var password = _driver.FindElement(MobileBy.AccessibilityId("PasswordEntry"));
password.SendKeys("pass123");
_driver.FindElement(MobileBy.AccessibilityId("LoginButton")).Click();
var welcome = _driver.FindElement(MobileBy.AccessibilityId("WelcomeLabel"));
Assert.Contains("Bem-vindo", welcome.Text);
}
public Task DisposeAsync() { _driver.Quit(); return Task.CompletedTask; }
}
Repare em MobileBy.AccessibilityId. Para que o Appium encontre elementos de forma estável, defina AutomationId em cada controle XAML. Esse identificador é projetado em content-desc no Android e em accessibilityIdentifier no iOS, e funciona em ambas as plataformas sem precisar de branches no código de teste. Eu já paguei caro por não fazer isso em um projeto anterior, quando metade dos seletores quebrou depois de um redesign de tela.
O que substituiu o Xamarin.UITest?
O Xamarin.UITest foi oficialmente descontinuado pela Microsoft em maio de 2023, junto com o serviço App Center, e não recebe atualizações para Android 14+ nem iOS 17+. Equipes que estão migrando do Xamarin.Forms para .NET MAUI precisam substituir essa infraestrutura. Em 2026, três alternativas dominam:
Appium 2 + Appium.WebDriver: opção mais madura, multi-plataforma e com comunidade ativa. Recomendada para a maioria dos projetos.
Microsoft.Maui.Automation (preview): API experimental da Microsoft que permite escrever testes em C# acessando a árvore visual do MAUI diretamente. Promete substituir o Xamarin.UITest com sintaxe mais limpa, mas em junho de 2026 ainda está em preview e eu não recomendaria para produção crítica.
Maestro: ferramenta YAML-first multi-plataforma popular em equipes que misturam React Native e MAUI. Excelente para fluxos simples, limitada para asserções complexas.
Se você tem uma suíte UITest legada grande, considere uma migração faseada. Mantenha os testes existentes em Xamarin.UITest rodando contra emuladores Android 13 / iOS 16 enquanto reescreve os fluxos críticos em Appium 2. Tentar fazer big-bang quase sempre acaba mal.
CI/CD: rodando testes em GitHub Actions
A maior vitória de testar .NET MAUI em 2026 é integrar tudo em CI. Um workflow típico no GitHub Actions cobre três jobs paralelos:
Para acelerar o pipeline, faça cache do workload MAUI (~/.dotnet/sdk-manifests) e do Gradle (~/.gradle/caches). Sem cache, instalar o workload sozinho consome 90 a 120 segundos por job, e isso some rápido se você tem dezenas de PRs por dia.
Cobertura de código e métricas
Coletar cobertura em projetos MAUI usa o mesmo Coverlet de qualquer projeto .NET. Adicione coverlet.collector ao projeto de testes e rode:
Metas realistas para 2026: 80%+ em Services e ViewModels, 60%+ em Models e helpers, e ignore Pages/handlers (que são testados pela camada de DeviceTests). Configure thresholds no .csproj para falhar o build se a cobertura cair, em vez de aceitar regressão silenciosa. É mais fácil convencer a equipe a tratar isso como bug do que reverter uma cultura de "depois a gente vê".
Armadilhas comuns ao testar .NET MAUI
Em centenas de revisões de código de projetos MAUI nos últimos dois anos, os mesmos padrões problemáticos aparecem:
Testar páginas em vez de ViewModels: instanciar new MyPage() em um teste força inicializar todo o sistema de handlers. Refatore para que toda lógica testável viva no ViewModel.
Mockar Connectivity.Current diretamente: a API estática Microsoft.Maui.Networking.Connectivity é difícil de mockar. Crie uma interface IConnectivityService e injete-a. Sua VM nunca deve tocar a estática.
Ignorar threading:MainThread.BeginInvokeOnMainThread não funciona em testes unitários. Abstraia em IDispatcher e injete uma versão "imediata" nos testes.
UI tests sem AutomationId: sem AutomationId em XAML, testes Appium dependem de XPath frágil que quebra a cada redesign. Adicionar AutomationId deve ser parte do code review padrão.
Não rodar em hardware real: emuladores escondem bugs específicos de fabricantes (Samsung One UI, Xiaomi MIUI). Eu peguei esse exato bug shippando um app de banco há um ano e meio: passava em todos os emuladores, quebrava em um Galaxy A53 específico. Use BrowserStack ou um device farm próprio para uma execução semanal em devices físicos.
Para apps com lógica de performance crítica, combine essa estratégia com benchmarks usando BenchmarkDotNet sobre o projeto Core. Veja como aplicar isso em conjunto com otimização de performance no .NET MAUI para detectar regressões antes que cheguem ao usuário. A documentação oficial em Microsoft Learn .NET MAUI mantém exemplos atualizados de DeviceTests no repositório de samples.
Perguntas Frequentes
Como testar uma aplicação .NET MAUI?
Use uma pirâmide de três camadas: testes unitários com xUnit ou NUnit sobre ViewModels e Services, testes de integração com Microsoft.Maui.TestUtils.DeviceTests em emulador, e testes end-to-end com Appium 2 contra um APK ou IPA instalado em um dispositivo real ou virtual.
Qual a diferença entre testes unitários e testes de UI no MAUI?
Testes unitários rodam em milissegundos no runtime .NET puro, sem renderização, e validam lógica isolada de classes como ViewModels e Services. Testes de UI rodam o app inteiro em um dispositivo, simulam toques e validam o comportamento visual e de navegação. São 100 a 1.000 vezes mais lentos e devem cobrir apenas fluxos críticos.
O Xamarin.UITest ainda funciona em 2026?
Tecnicamente o pacote ainda instala, mas a Microsoft descontinuou suporte oficial em 2023 e ele não funciona com Android 14+ nem iOS 17+ de forma confiável. Migre para Appium 2 com o cliente Appium.WebDriver, ou avalie Microsoft.Maui.Automation (ainda em preview).
Como configurar Appium 2 para testar um app .NET MAUI?
Instale o servidor Appium 2 via npm, adicione os drivers uiautomator2 (Android) e xcuitest (iOS), gere um APK ou IPA do seu app MAUI, e crie um projeto xUnit que usa o pacote NuGet Appium.WebDriver para abrir uma sessão apontando para http://127.0.0.1:4723. Defina AutomationId em todos os controles XAML para localizá-los de forma estável.
É possível medir cobertura de código em projetos .NET MAUI?
Sim. Adicione o pacote coverlet.collector ao projeto de testes e rode dotnet test --collect:"XPlat Code Coverage". Para visualizar, instale a ferramenta global dotnet-reportgenerator-globaltool e gere relatórios HTML. Foque em medir cobertura do projeto Core (Services, ViewModels) e ignore o projeto MAUI principal, que é melhor coberto por DeviceTests.