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.