allanlotta
~/blog/what-ai-powered-qa-looks-like

Agentes que testam apps mobile como usuários reais

"QA com IA" normalmente quer dizer um modelo que escreve testes unitários a partir da assinatura de uma função. Isso é um gerador de código. QA é alguém abrindo o app, fazendo algo um pouco errado e notando o que quebra.

Eu queria que agentes fizessem essa parte. Não raciocinar sobre o código, mas segurar o telefone. Este é o sistema que tornou isso possível: dispositivos reais e simuladores, cenários escritos como comportamento, automação visual e uma camada de orquestração que transforma "testa essa release" em tickets e correções enquanto eu durmo.

O loop

 ┌──────────┐   ┌──────────┐   ┌──────────┐   ┌──────────┐
 │  spec /  │──▶│ escreve  │──▶│ roda nos │──▶│ observa  │
 │ mudança  │   │ cenário  │   │aparelhos │   │ tela +   │
 └──────────┘   └──────────┘   └──────────┘   │ logs     │
       ▲                                      └────┬─────┘
       │                                           │
 ┌─────┴────┐   ┌──────────┐   ┌──────────┐   ┌────▼─────┐
 │ melhora  │◀──│ aprende  │◀──│fix + PR +│◀──│reproduz  │
 │ tooling  │   │ por app  │   │ regressão│   │ 2x, abre │
 └──────────┘   └──────────┘   └──────────┘   │ ticket   │
                                              └──────────┘

Uma mudança entra. Um agente escreve ou escolhe os cenários que ela afeta. Os cenários rodam em uma matriz de dispositivos. O agente observa a tela e os logs, não só o código de saída. Qualquer coisa errada é reproduzida uma segunda vez antes de virar ticket. O ticket vem com passos, uma gravação e trechos de log. Depois o agente propõe a correção, adiciona a regressão, anota o que aprendeu sobre esse app e conserta qualquer ferramenta que tenha deixado a rodada mais difícil do que deveria.

As camadas

              ┌──────────────────────────────┐
              │  eu: charters, prioridades,  │
              │  revisão de tickets e PRs    │
              └──────────────┬───────────────┘
                             │
              ┌──────────────▼───────────────┐
              │planejador ─▶ workers ─▶ juiz │
              └───────┬──────────────┬───────┘
                      │              │
        ┌─────────────▼────┐   ┌─────▼──────────────┐
        │      memória     │   │    ferramentas     │
        │                  │   │                    │
        │ cenários (BDD)   │   │ flows Maestro      │
        │ manias por app   │   │ adb / simctl       │
        │ bugs conhecidos  │   │ screenshot + a11y  │
        │ matriz de devices│   │ taps de log e rede │
        └─────────────┬────┘   │ lease de device    │
                      │        └─────┬──────────────┘
              ┌───────▼──────────────▼───────┐
              │          avaliação           │
              │                              │
              │  reproduz duas vezes         │
              │  diff de screenshot          │
              │  suíte de regressão          │
              │  tracker de flakes           │
              └──────────────────────────────┘

Três papéis de agente. Um planejador transforma um charter em cenários e os distribui entre os dispositivos. Workers rodam os cenários, um dispositivo cada, e reportam. Um juiz lê cada achado, tenta reproduzir em um dispositivo diferente e ou registra ou descarta. Eu escrevo charters e leio o que sobrevive ao juiz.

Cenários como comportamento, não cliques

Scripts que dizem "toque em 340,812" quebram no próximo tamanho de tela. Cenários dizem o que um usuário faz e espera.

 Funcionalidade: comprar uma posição
   Cenário: usuário deslogado em rede lenta
     Dado que o app está recém-instalado
       E a rede está limitada a 3G
     Quando abro um mercado e toco em Comprar
     Então vejo a tela de login
       E depois do login volto ao mesmo mercado
       E o botão de compra fica habilitado em até 3 segundos

O agente transforma isso em um flow do Maestro: toque por label de acessibilidade, asserção por texto visível, espera por condição. O flow é commitado ao lado da feature. Utilitários que todo flow precisa, como login, onboarding e esconder o teclado, são compartilhados, e o do teclado existe duas vezes porque iOS e Android escondem o teclado de jeitos diferentes. Esse detalhe foi aprendido por um agente em uma rodada que falhou e virou um arquivo.

O agente também escreve os cenários que ninguém pede: girar a tela no meio do flow, ir para o background e voltar, matar e reabrir, revogar uma permissão, deep link para uma tela que exige login, rodar com a maior fonte, rodar em português. Foi aí que a maioria dos bugs de verdade estava.

O que uma phone farm faz

Uma phone farm é um rack de dispositivos, hubs USB e uma máquina que os controla. O uso clássico é volume: instalar este build em vinte telefones, rodar o mesmo flow em todos, coletar os screenshots.

 host ──▶ adb (usb / tcp) ──▶ device 01  Android 12, 5.5"
      ──▶ adb              ──▶ device 02  Android 14, 6.7"
      ──▶ adb              ──▶ device 03  Android 15, tablet
      ──▶ simctl           ──▶ iPhone SE, iOS 17
      ──▶ simctl           ──▶ iPhone 16 Pro, iOS 18
      ──▶ simctl           ──▶ iPad, iOS 18

 por device: instalar, abrir, resetar estado, definir locale,
             limitar rede, conceder / revogar permissões,
             screenshot, gravar, puxar logs, reiniciar

Tudo acima é um comando. Instalar e desinstalar um build. Apagar os dados do app para cada rodada começar do zero. Definir o locale, a escala da fonte, o fuso horário. Limitar a rede. Conceder ou revogar câmera, notificações, localização. Tocar, deslizar, digitar, apertar voltar. Screenshot, gravação de tela, puxar o logcat e os relatórios de crash. Reiniciar quando um dispositivo fica estranho, o que acontece.

A farm também cuida da camada física chata: dispositivos que caem do USB, baterias que estufam se ficam em 100%, throttling térmico que faz um telefone rápido parecer lento, um dispositivo travado em um diálogo do sistema bloqueando todas as rodadas. Cada um desses ganhou um detector e uma recuperação, porque uma farm que precisa de uma pessoa para desplugar um cabo não é uma farm.

O que muda quando o operador é um agente

Uma farm operada por scripts responde à pergunta que você escreveu. Uma farm operada por agentes responde à pergunta que você deveria ter feito.

  • Ela lê a tela. Screenshot mais árvore de acessibilidade, a cada passo. Um flow pode passar enquanto a tela mostra uma lista vazia, uma moeda errada ou um botão renderizado embaixo do teclado. Scripts não percebem. O agente percebe.
  • Ela explora. Com um charter, não um script, ela tenta a coisa um pouco errada: toque duplo no enviar, voltar durante um loading, colar com um espaço no final no campo de valor.
  • Ela reproduz antes de reportar. Duas vezes, em um segundo dispositivo quando possível. Flaky é uma categoria, não um achado, e flakes vão para um tracker que aponta para a ferramenta ou para a condição de espera, não para o app.
  • Ela abre tickets que um desenvolvedor consegue atacar. Passos, dispositivo, OS, build, uma gravação, a janela de log em torno da falha e um palpite sobre a causa com o arquivo suspeito.
  • Ela corrige. Para uma boa parte dos achados, o mesmo agente abre um PR com a correção e o flow de regressão que teria pego o bug. Eu reviso o diff, não o bug.
  • Ela lembra. Manias de cada app, bugs conhecidos, quais telas são lentas, qual dispositivo é instável. A próxima rodada começa com isso.
 achado no device 02
   │
   ├─ reproduz no device 05 ──▶ falha de novo ──▶ ticket + PR
   │                       └──▶ passa ──▶ flake tracker
   │                                         └─▶ corrige o wait,
   │                                             não o app
   └─ registra: build, passos, vídeo, logcat, arquivo suspeito

Números que importaram

Antes disso, uma release tomava de uma pessoa quase um dia inteiro clicando pelos mesmos flows em dois telefones. Depois, a matriz acima roda a cada merge, sem supervisão, e uma pessoa lê uma lista curta de achados confirmados de manhã. A medida interessante não é velocidade. É que os bugs passaram a chegar com uma reprodução e uma proposta de correção em vez de um screenshot num chat.

Por que isso não é sobre telefones

 QA mobile                     qualquer QA
 ─────────────────────────     ─────────────────────────────
 phone farm                    os ambientes em que você testa
 cenário BDD                   comportamento, não coordenadas
 screenshot + árvore a11y      observar a saída, não o código
 reproduzir duas vezes         um achado é fato, não palpite
 flake tracker                 culpe a ferramenta antes do produto
 fix + regressão em um PR      o bug se fecha sozinho
 memória por app               a próxima rodada começa mais sábia

A parte difícil nunca foi controlar um telefone. O ADB faz isso há quinze anos. Foi dar aos agentes um jeito de ver, um jeito de dizer o que deveria acontecer, um lugar para lembrar e um juiz entre eles e a fila de tickets.

O que eu fiz

Construí a farm e as ferramentas de dispositivo, escrevi os primeiros cenários e os flows compartilhados, defini a regra de que nada é registrado sem uma segunda reprodução e desenhei a divisão entre planejador, worker e juiz. Depois parei de clicar por releases e comecei a ler tickets.