Xamarin.UITest

Importante

O Visual Studio App Center está programado para ser desativado em 31 de março de 2025. Embora você possa continuar a usar o Visual Studio App Center até que ele seja totalmente desativado, há várias alternativas recomendadas para as quais você pode considerar migrar.

Saiba mais sobre linhas do tempo e alternativas de suporte.

O Xamarin.UITest é uma estrutura de teste em C# que usa o NUnit para testes de aceitação da interface do usuário em aplicativos iOS e Android. Ele se integra fortemente aos projetos Xamarin.iOS e Xamarin.Android, mas também pode ser usado com projetos nativos do iOS e do Android. O Xamarin.UITest é a Biblioteca de Automação que permite que os testes NUnit sejam executados em dispositivos Android e iOS. Os testes interagem com a interface do usuário como um usuário faria: inserir texto, tocar botões e gestos, como passar o dedo.

Normalmente, cada Xamarin.UITest é escrito como um método chamado [Test]. A classe que contém o teste é conhecida como .[TestFixture] O acessório de teste contém um único teste ou grupo de testes. O acessório também é responsável pela configuração para fazer com que o teste seja executado e a limpeza que precisa ser feita quando o teste for concluído. Cada teste deve seguir o padrão Arrange-Act-Assert :

  1. Organizar: o teste configurará as condições e inicializará as coisas para que o teste possa ser acionado.
  2. Agir: o teste interagirá com o aplicativo, inserirá texto, botões de push e assim por diante.
  3. Assert: o teste examina os resultados das ações executadas na etapa Act para determinar a exatidão. Por exemplo, o aplicativo pode verificar se uma mensagem de erro específica é exibida.

O melhor momento para começar a usar o Xamarin.UITest é durante o desenvolvimento de um aplicativo móvel. Os testes automatizados são escritos como um recurso que está sendo desenvolvido de acordo com as etapas descritas na lista a seguir:

  1. Desenvolva o recurso no aplicativo Android ou iOS.
  2. Escreva os testes e execute-os localmente para verificar a funcionalidade.
  3. Crie uma nova Execução de Teste no Teste do App Center ou use uma Execução de Teste existente.
  4. Compile o IPA ou o APK e carregue-o junto com os testes no Teste do App Center.
  5. Corrija os problemas ou bugs expostos pelo Teste do App Center.
  6. Repita o processo passando para o próximo recurso para o aplicativo.

Para aplicativos existentes que não estão mais em desenvolvimento ativo, pode não ser econômico adicionar retroativamente testes automatizados. Em vez disso, uma abordagem melhor é usar Xamarin.UITest ao corrigir bugs. Por exemplo, considere um aplicativo que não tem nenhum teste automatizado e um usuário relata um bug. Um desenvolvedor atribuído para corrigir esse bug pode executar algumas (ou todas) das seguintes ações:

  • Verifique o bug ou a regressão manualmente.
  • Escreva um teste usando Xamarin.UITest que demonstre o bug.
  • Envie o teste para o teste do App Center para obter algumas informações sobre o escopo e o impacto do bug em dispositivos relevantes.
  • Corrija o bug.
  • Prove que o bug foi corrigido com um Xamarin.UITest aprovado.
  • Envie as correções e teste para o Teste do App Center para verificar se o bug foi corrigido nos dispositivos relevantes.
  • Verifique os testes aprovados no controle de versão.

O teste automatizado de interface do usuário depende muito da localização e da interação com exibições na tela. O Xamarin.UITest atende a esse requisito com dois conjuntos importantes de APIs que funcionam entre si:

  1. Ações que podem ser feitas em exibições – O Xamarin.UITest fornece APIs que permitem que um teste simule ações comuns do usuário, como tocar no modo de exibição, inserir texto ou deslizar o dedo no modo de exibição.
  2. Consultas para localizar exibições na tela – parte da estrutura Xamarin.UITest são APIs que localizarão os modos de exibição em uma tela. As consultas localizam exibições em tempo de execução inspecionando atributos para a exibição e retornando um objeto no qual as ações podem funcionar. Consultar dessa maneira é uma técnica poderosa que permite que os testes sejam gravados para interfaces do usuário, seja qual for o tamanho, a orientação ou o layout da tela

Para ajudar na gravação de testes, o Xamarin.UITest fornece um REPL (read-eval-print-loop). O REPL permite que desenvolvedores e testadores interajam com uma tela enquanto o aplicativo está em execução e simplifica a criação das consultas.

Apresentando a API Xamarin.UITest

Todas as interações de teste com o aplicativo móvel ocorrem por meio de uma instância do Xamarin.UITest.IApp. Essa interface define os métodos cruciais para que o teste colabore com o aplicativo e interaja com a interface do usuário. Há duas implementações concretas dessa interface:

  • Xamarin.UITest.iOS.iOSApp Essa classe automatizará testes no iOS.
  • Xamarin.UITest.Android.AndroidApp Essa classe é para automatizar testes no Android.

iOSApp os objetos e AndroidApp não são instanciados diretamente. Em vez disso, eles são criados usando a classe auxiliar ConfigureApp . Essa classe é um construtor que garante que o iOSApp ou AndroidApp seja instanciado corretamente.

É recomendável usar uma nova IApp instância para cada teste. Uma nova instância impede que o estado de um teste seja despejado em outro. Há dois locais em que um teste NUnit pode inicializar uma instância do IApp:

  • SetUp No método Normalmente, um acessório de teste é um agrupamento lógico de testes relacionados, cada um deles executando independentemente do outro. Nesse cenário, o IApp deve ser inicializado no SetUp método , garantindo que um novo IApp esteja disponível para cada teste.
  • TestFixtureSetup No método Em algumas situações, um único teste pode exigir seu próprio acessório de teste. Nesse caso, pode fazer mais sentido inicializar o IApp objeto uma vez no TestFixtureSetup método .

Depois de IApp configurado, um teste pode começar a interagir com o aplicativo que está sendo testado. Para fazer isso, é necessário obter referências aos modos de exibição visíveis na tela. Muitos métodos em Xamarin.UITest tomam um Func<AppQuery, AppQuery> parâmetro para localizar as exibições. Por exemplo, o snippet a seguir mostra como tocar em um botão:

app.Tap(c=>c.Button("ValidateButton"));

Há duas implementações da IApp interface dentro da estrutura Xamarin.UITest, uma para iOS e outra para Android.

Inicializar o IApp para aplicativos iOS

Quando o Xamarin.UITest executa um teste no iOS, ele inicia uma instância do simulador do iOS, implanta o aplicativo, inicia-o e começa a executar os testes. O aplicativo iOS já deve ser criado. O Xamarin.UITest não compila o aplicativo e cria o Pacote de Aplicativos para você.

O AppBundle método pode ser usado para especificar onde no sistema de arquivos o pacote do aplicativo pode ser encontrado. Há duas maneiras de fazer isso, com um caminho absoluto ou um caminho relativo. Este snippet de código mostra o uso de um caminho absoluto para o pacote do aplicativo:

IApp app = ConfigureApp
    .iOS
    .AppBundle("/path/to/iosapp.app")
    .StartApp();

Os caminhos parciais devem ser relativos ao assembly Xamarin.UITest. Este snippet é um exemplo:

IApp app = ConfigureApp
    .iOS
    .AppBundle("../../../iOSAppProject/bin/iPhoneSimulator/Debug/iosapp.app")
    .StartApp();

O exemplo de caminho relativo informa AppBundle para subir três diretórios do assembly Xamarin.UITest e, em seguida, navegar pela árvore de projeto do projeto de aplicativo iOS para localizar o pacote de aplicativos.

ConfigureApp tem outros métodos para ajudar a configurar IAppo . Consulte a classe iOSAppConfigurator para obter mais detalhes. Alguns dos métodos mais interessantes são descritos na tabela a seguir:

Método Descrição
AppBundle Esse método especifica o caminho para o pacote de aplicativos a ser usado durante o teste.
Debug Esse método habilitará a depuração de mensagens de log no executor de teste. Esse método é útil para solucionar problemas com a execução do aplicativo no simulador.
DeviceIdentifier Configura o dispositivo a ser usado com o identificador do dispositivo. Esse método será descrito mais detalhadamente abaixo.
EnableLocalScreenshots Habilite capturas de tela ao executar testes localmente. As capturas de tela são sempre habilitadas quando os testes estão em execução na nuvem.

Para obter mais informações sobre como executar testes do iOS em um simulador de iOS específico, consulte Determinar a ID do dispositivo para um simulador do iOS.

Inicializar o IApp para aplicativos Android

O Xamarin.UITest implantará um APK existente em um dispositivo anexado ou uma instância do emulador android que já está em execução. O aplicativo será iniciado e, em seguida, o teste será executado. O Xamarin.UITest não pode compilar o APK nem pode iniciar uma instância do android emulator.

O ApkFile método de IApp é usado para especificar onde no sistema de arquivos o APK pode ser encontrado. Há duas maneiras de fazer isso, com um caminho absoluto ou um caminho relativo. Este snippet de código mostra o uso de um caminho absoluto para o APK:

IApp app = ConfigureApp
    .Android
    .ApkFile("/path/to/android.apk")
    .StartApp();

Os caminhos parciais devem ser relativos ao assembly Xamarin.UITest. Este snippet é um exemplo:

IApp app = ConfigureApp
    .Android
    .ApkFile("../../../AndroidProject/bin/Debug/android.apk")
    .StartApp();

O exemplo de caminho relativo informa ApkFile para subir três diretórios do assembly Xamarin.UITest e, em seguida, navegar pela árvore de projeto do projeto de aplicativo Android para localizar o arquivo apk.

Se houver mais de um dispositivo ou emulador conectado, o Xamarin.UITest interromperá a execução do teste e exibirá uma mensagem de erro, pois não é possível resolve qual é o destino pretendido para o teste. Nesse caso, é necessário fornecer a ID serial do dispositivo ou emulador para executar o teste. Por exemplo, considere a seguinte saída do adb devices comando que lista todos os dispositivos (ou emuladores) anexados ao computador (juntamente com sua ID serial):

$ adb devices
List of devices attached
192.168.56.101:5555 device
03f80ddae07844d3    device

O dispositivo pode ser especificado usando o DeviceSerial método :

IApp app = ConfigureApp.Android.ApkFile("/path/to/android.apk")
                               .DeviceSerial("03f80ddae07844d3")
                               .StartApp();

Interagindo com a interface do usuário

Para interagir com exibições, muitos IApp métodos levam um Func<AppQuery, AppQuery> delegado para localizar a exibição. Esse delegado usa AppQuery que está no centro de como o Xamarin.UITest localiza exibições.

AppQuery é uma interface fluente para criar as consultas para localizar exibições. Dos métodos que AppQuery fornece, o Marked método é um dos mais simples e flexíveis. Esse método usa uma heurística para tentar localizar exibições e será discutido mais detalhadamente na seção a seguir. Por enquanto, é importante entender que IApp tem muitos métodos para interagir com um aplicativo. Esses métodos usam um Func<AppQuery, AppQuery> para obter uma referência à exibição com a qual interagir. Alguns dos métodos mais interessantes fornecidos por AppQuery estão listados abaixo:

Método Descrição
Button Localizará um ou mais botões na tela.
Class Tentará localizar exibições que são de uma classe especificada.
Id Tentará localizar uma exibição com a ID especificada.
Index . Retornará uma exibição de uma coleção de exibições correspondentes. Normalmente usado em conjunto com outros métodos. Usa um índice baseado em zero.
Marked Retornará uma exibição de acordo com a heurística discutida abaixo.
Text Corresponderá aos modos de exibição que contêm o texto fornecido.
TextField Corresponderá a um Android EditText ou iOS UITextField.

Por exemplo, o método a seguir mostra como simular um toque em um botão chamado "SaveUserdataButton":

app.Tap(c=>c.Marked("SaveUserDataButton"));

Como AppQuery é uma interface fluente, é possível encadear várias invocações de método. Considere este exemplo mais complicado de tocar em uma exibição:

app.Tap(c=>c.Marked("Pending")
            .Parent()
            .Class("AppointmentListCell").Index(0));

Aqui, o AppQuery primeiro encontrará uma exibição marcada como Pendinge, em seguida, selecionará o primeiro pai dessa exibição que é um AppointmentListCell tipo.

Pode ser complicado tentar criar essas consultas examinando um aplicativo móvel. O Xamarin.UITest fornece um REPL que pode ser usado para explorar a hierarquia de exibição de uma tela, experimentar a criação de consultas e usá-las para interagir com um aplicativo.

Usando o REPL

A única maneira de iniciar o REPL é invocar o IApp.Repl método dentro de um teste existente. Isso requer a criação de um NUnit TestFixture, configurando uma instância do IApp que pode ser usada em um Test método . O snippet de código a seguir mostra um exemplo de como fazer isso:

[TestFixture]
public class ValidateCreditCard
{
    IApp app;

    [SetUp]
    public void Setup()
    {
        app = ConfigureApp.Android.ApkFile("/path/to/application.apk").StartApp();
    }
    [Test]
    public void CreditCardNumber_TooLong_DisplayErrorMessage()
    {
        app.Repl();
    }
}

Para executar o teste, clique com o botão direito do mouse na medianiz do Visual Studio e selecione Executar:

Captura de tela do menu pop-up com as opções de execução para um teste

O teste será executado e, quando o método for invocado, o Repl Xamarin.UITest iniciará o REPL em uma sessão de terminal, conforme mostrado na captura de tela a seguir:

Captura de tela do terminal do macOS executando o REPL do Xamarin.UITest

O REPL inicializou uma instância do IApp chamada app, que interage com o aplicativo. Uma das primeiras coisas a fazer é explorar a interface do usuário. O REPL tem um tree comando para fazer isso. Ele imprimirá a hierarquia de exibições na tela exibida. Por exemplo, considere a seguinte captura de tela de um aplicativo:

Captura de tela de um aplicativo de exemplo em execução em um iPhone

Podemos usar o tree comando para exibir a seguinte hierarquia desta tela:

App has been initialized to the 'app' variable.
Exit REPL with ctrl-c or see help for more commands.

>>> tree
[UIWindow > UILayoutContainerView]
  [UINavigationTransitionView > ... > UIView]
    [UITextView] id: "CreditCardTextField"
      [_UITextContainerView]
    [UIButton] id: "ValidateButton"
      [UIButtonLabel] text: "Validate Credit Card"
    [UILabel] id: "ErrorrMessagesTestField"
  [UINavigationBar] id: "Credit Card Validation"
    [_UINavigationBarBackground]
      [_UIBackdropView > _UIBackdropEffectView]
      [UIImageView]
    [UINavigationItemView]
      [UILabel] text: "Credit Card Validation"
>>>

Podemos ver que há um UIButton nessa exibição com o id de ValidateButton. Podemos usar as informações exibidas pelo tree comando para ajudar a criar as consultas necessárias para localizar e interagir com exibições. Por exemplo, o código a seguir simula um toque no botão:

app.Tap(c=>c.Marked("ValidateButton"))

À medida que os comandos estão sendo inseridos, eles são lembrados pelo REPL em um buffer. O REPL fornece um copy comando que copiará o conteúdo desse buffer para a área de transferência. Isso nos permite criar um protótipo de um teste. Podemos copiar o trabalho feito no REPL para a área de transferência com copye colar esses comandos dentro de um [Test].

Usando Marcado para localizar modos de exibição

O método AppQuery.Marked é uma maneira conveniente e poderosa de consultar exibições na tela. Ele funciona inspecionando a hierarquia de exibição de um modo de exibição na tela, tentando corresponder as propriedades no modo de exibição com a cadeia de caracteres fornecida. Marked funciona de forma diferente dependendo do sistema operacional.

Localizando modos de exibição do iOS com Marcado

As exibições do iOS estarão localizadas usando um dos seguintes atributos:

  • o AccessibilityIdentifier do modo de exibição
  • o AccessibilityLabel do modo de exibição

Por exemplo, considere o seguinte snippet de código C# que cria um UILabel e define o AccessibilityLabel:

UILabel errorMessagesTextField = new UILabel(new RectangleF(10, 210, 300, 40));
errorMessagesTextField.AccessibilityLabel = "ErrorMessagesTextField";
errorMessagesTextField.Text = String.Empty;

Essa exibição pode ser localizada pela seguinte consulta:

AppResult[] results = app.Marked("ErrorMessagesTextField");

Localizando modos de exibição do Android com Marcado

As exibições do Android estarão localizadas com base em uma das seguintes propriedades:

  • o Id do modo de exibição
  • o ContentDescription do modo de exibição
  • o Text de uma exibição

Por exemplo, considere um layout do Android que tenha o seguinte botão definido:

<Button
    android:text="Action 1"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/action1_button"
    android:layout_weight="1"
    android:layout_marginLeft="5dp" />

Podemos ver que o android:id deste botão é action1_button e que é a android:textAção 1. Uma das duas consultas a seguir localizará o botão na tela:

  • app.Query(c=>c.Marked("action1_button"));
  • app.Query(c=>c.Marked("Action 1"));

Controlando o aplicativo com Xamarin.UITest.IApp

Depois de IApp configurado e inicializado, o teste pode começar a interagir com o aplicativo. Um exemplo de um método usando Func<AppQuery, AppQuery> é o IApp.Query() método . Esse método executará a consulta e retornará os resultados. O exemplo mais simples é mostrado no snippet a seguir, que retorna uma lista de todos os modos de exibição visíveis na tela:

AppResult[] results = app.Query(c=>c.All())

A tabela a seguir demonstra alguns outros exemplos de como usar AppQuery para localizar exibições na tela:

Sintaxe Resultados
app.Query(c=>c.Class("UILabel")) O .Class() método consultará exibições que são uma subclasse de um iOS UILabel.
app.Query(c=>c.Id("txtUserName")) O .Id() método consultará exibições com um Id de txtUserName.
app.Query(c=>c.Class("UILabel").Text("Hello, World")) Localiza todas as UILabel classes que têm o texto "Olá, Mundo".
results = app.Query(c=>c.Marked("ValidateButton")) Retorna todos os modos de exibição marcados com o texto especificado. O Marked método é um método útil que pode simplificar consultas. Ele será abordado na seção a seguir.

A próxima tabela lista alguns (mas não todos) dos métodos fornecidos por IApp que podem ser usados para interagir ou manipular exibições na tela:

Exemplo Descrição
PressEnter Pressione a tecla Enter no aplicativo.
Tap Simula um gesto de toque/toque no elemento correspondente.
EnterText Insere texto no modo de exibição. Em um aplicativo iOS, o Xamarin.UITest inserirá o texto usando o teclado virtual. Por outro lado, o Xamarin.UITest não usará o teclado Android, ele inserirá diretamente o texto no modo de exibição.
WaitForElement Pausa a execução do teste até que as exibições apareçam na tela.
Screenshot(String) Captura uma captura de tela do aplicativo em seu estado atual e o salva em disco. Ele retorna um FileInfo objeto com informações sobre a captura de tela tirada.
Flash Esse método fará com que o modo de exibição selecionado "flash" ou "flicker" na tela.

Para obter mais informações sobre a IApp interface , consulte a documentação da API para IApp, AndroidAppe iOSApp.

Como exemplo de como usar esses métodos, considere o teste a seguir para a captura de tela que foi exibida acima. Esse teste inserirá um número de 17 dígitos para um cartão de crédito em um campo de texto e, em seguida, tocará em um botão na tela. Em seguida, ele inspecionará a tela em busca de uma mensagem de erro informando ao usuário que o número é muito longo para ser um número de cartão de crédito válido:

[Test]
public void CreditCardNumber_TooLong_DisplayErrorMessage()
{
    /* Arrange - set up our queries for the views */
    // Nothing to do here, app has been instantiated in the [SetUp] method.

    /* Act */
    app.EnterText(c => c.Marked("CreditCardTextField"), new string('9', 17));
    // Screenshot can be used to break this test up into "steps".
    // The screenshot can be inspected after the test run to verify
    // the visual correctness of the screen.
    app.Screenshot("Entering a 17 digit credit card number.");

    app.Tap(c => c.Marked("ValidateButton"));
    app.Screenshot("The validation results.");

    /* Assert */
    AppResult[] result = app.Query(c => c.Class("UILabel").Text("Credit card number is too long."));
    Assert.IsTrue(result.Any(), "The error message isn't being displayed.");
}

Esse teste também usa o Screenshot método para tirar fotos em pontos-chave durante a execução do teste. Quando esse teste for executado, o App Center fará as capturas de tela e as exibirá nos resultados do teste. O método permite dividir um teste em etapas e fornecer descrições para as capturas de tela.