Neste guia, você vai ver como dev e QA podem trabalhar como um time só, e não como duas etapas de uma esteira. Vamos falar de critérios de aceite claros no formato Dado/Quando/Então, de uma Definition of Done que inclui testes, da pirâmide de testes, de como levar o QA para o refinamento, de PRs com evidência e passos de teste, de testes de unidade que cobrem as bordas, de bug reports úteis e de ambientes reproduzíveis. No fim, há um template de PR pronto para copiar.
Por que “shift-left” é um assunto de dev
Shift-left é a ideia de trazer a qualidade para o começo do ciclo: discutir riscos antes de codar, testar enquanto se desenvolve e não deixar a descoberta de problemas para o fim da sprint. Trabalho com QA desde 2010 e o padrão se repete: quando o teste só acontece depois do “pronto”, o bug volta para o dev com o contexto já esfriado, a correção custa mais e a relação entre as pessoas desgasta. Quando dev e QA conversam cedo, boa parte desses bugs nem chega a ser escrita.
Isso não significa que o dev vira QA ou que o QA vira dev. Significa que qualidade é responsabilidade compartilhada, e que cada um contribui com o que faz melhor: o dev conhece o código e suas fragilidades; o QA enxerga o comportamento, os riscos de negócio e os caminhos que ninguém pensou.
Critérios de aceite claros com Dado/Quando/Então
Um critério de aceite responde a uma pergunta simples: como saberemos que essa história está pronta do ponto de vista de quem usa? O formato Dado/Quando/Então (o Given/When/Then do BDD) ajuda porque obriga a descrever contexto, ação e resultado observável, sem espaço para “deve funcionar corretamente”.
Funcionalidade: Login na loja
Cenário: Usuário bloqueado tenta entrar
Dado que existe o usuário "locked_user" com status bloqueado
Quando ele informa usuário e senha válidos e clica em "Entrar"
Então a mensagem "Usuário bloqueado" é exibida
E ele permanece na tela de login
E nenhum token de sessão é gerado
Cenário: Senha em branco
Dado que estou na tela de login
Quando informo o usuário "standard_user" e deixo a senha vazia
Então o botão "Entrar" fica desabilitado
Algumas regras práticas para critérios que realmente ajudam:
- Um comportamento por cenário. Se o “Então” tem cinco coisas sem relação, provavelmente são cenários diferentes.
- Resultado observável. Mensagem na tela, status HTTP, registro no banco, evento publicado. Algo que dá para verificar.
- Inclua os caminhos tristes. Erro de validação, permissão negada, timeout, lista vazia. É ali que mora a maioria dos bugs.
- Linguagem de negócio, não de implementação. “Clico em Entrar” envelhece melhor que “chamo o endpoint X com o payload Y”.
Definition of Done que inclui testes
Critério de aceite é específico de cada história. A Definition of Done (DoD) é o acordo do time que vale para todas. Se a DoD diz só “código revisado e mergeado”, o teste vira opcional na prática. Uma DoD que puxa a qualidade para dentro do desenvolvimento costuma ter itens como:
- Todos os critérios de aceite verificados, com evidência no PR.
- Testes de unidade escritos para a lógica nova, incluindo casos de borda.
- Testes de integração ou de API atualizados quando o contrato mudou.
- Pipeline verde, sem testes desabilitados “temporariamente” sem justificativa.
- Nenhum bug crítico ou alto em aberto relacionado à história.
- Documentação (OpenAPI, README, notas de release) atualizada quando aplicável.
O importante é que a DoD seja curta, visível e revisada pelo time de tempos em tempos. Item que ninguém cumpre deve ser discutido, não ignorado em silêncio.
A pirâmide de testes como mapa de responsabilidades
A pirâmide de testes sugere muitos testes rápidos e baratos na base, uma camada intermediária de integração e poucos testes de ponta a ponta no topo. Ela não é uma regra matemática, mas é um bom mapa para dividir o trabalho:
- Base (unidade): responsabilidade natural do dev. Valida regras, cálculos, validações e bordas sem subir banco ou navegador.
- Meio (integração e API): território compartilhado. Contratos entre serviços, persistência, autenticação, mensagens de erro da API.
- Topo (E2E e exploratório): onde o QA costuma liderar, cobrindo fluxos críticos de ponta a ponta e explorando o que nenhum script previu.
O anti-padrão clássico é a “casquinha de sorvete”: quase nada de unidade e uma montanha de testes de interface lentos e instáveis. Se uma regra pode ser testada na unidade, teste ali e deixe o E2E para provar que as peças se conectam.
Envolver o QA no refinamento
O momento mais barato para encontrar um bug é antes de ele existir. No refinamento, o QA faz as perguntas que transformam uma história vaga em algo testável: “e se o usuário estiver bloqueado?”, “qual o limite de caracteres?”, “o que acontece se a API demorar?”, “isso vale para mobile também?”.
Uma prática que funciona bem é a conversa a três (às vezes chamada de “três amigos”): produto, dev e QA olham juntos a história e saem com os critérios de aceite escritos. Se o time não tem cerimônia formal para isso, quinze minutos antes de puxar a história já resolvem muito.
Testes de unidade que cobrem as bordas
Teste de unidade que só valida o caminho feliz dá falsa segurança. Os bugs aparecem nas bordas, então vale ter um checklist mental:
- Valores limite: mínimo, máximo, um a menos, um a mais.
- Vazios e nulos: string vazia, lista vazia, campo ausente, null.
- Formatos: acentos, emojis, espaços extras, CPF com e sem máscara.
- Datas e fusos: virada de dia, ano bissexto, horário de verão, timestamps em segundos versus milissegundos.
- Erros esperados: a função lança a exceção certa com a mensagem certa?
Testes parametrizados ajudam a cobrir essas variações sem duplicar código. Para montar a massa, o gerador de massa de testes e o validador de CPF poupam tempo, e o conversor de timestamp ajuda a conferir os casos de data.
PR com evidência e passos de teste
Um PR que diz só “corrige bug do login” obriga o revisor e o QA a redescobrir tudo. Um bom PR conta o que mudou, por que mudou, como testar e mostra que já foi testado. A evidência pode ser um print, um GIF curto, a saída do teste, um request e response da API ou um trecho de log. Para payloads grandes, formatar com o formatador de JSON ou mostrar o antes e depois com o comparador de JSON deixa a revisão muito mais rápida.
Este é um template que você pode salvar como .github/pull_request_template.md (ou o equivalente da sua plataforma):
## O que muda
<!-- Resumo em 1 a 3 linhas. Link da história/ticket. -->
## Por que
<!-- Contexto do problema ou da regra de negócio. -->
## Critérios de aceite atendidos
- [ ] Cenário 1: ...
- [ ] Cenário 2: ...
## Como testar
1. Ambiente/branch: ...
2. Pré-condições (usuário, massa, feature flag): ...
3. Passos: ...
4. Resultado esperado: ...
## Evidências
<!-- Prints, GIF, saída dos testes, request/response, logs. -->
## Testes automatizados
- [ ] Unidade (incluindo casos de borda)
- [ ] Integração/API
- [ ] E2E (se aplicável)
## Riscos e impacto
<!-- Áreas afetadas, migrações, flags, rollback. -->
## Checklist da DoD
- [ ] Pipeline verde
- [ ] Sem testes desabilitados sem justificativa
- [ ] Documentação atualizada
Como escrever um bug report que ajuda
Bug report bom é aquele que o dev consegue reproduzir na primeira tentativa, sem precisar perguntar nada. O mínimo:
- Título descritivo: onde, o quê e em que condição. “Checkout aceita cupom expirado no front React” diz muito mais que “cupom com erro”.
- Ambiente: URL, versão ou commit, navegador, dispositivo, usuário usado.
- Passos numerados a partir de um estado conhecido.
- Resultado esperado versus obtido, de preferência ligado ao critério de aceite.
- Evidências: print, vídeo, request/response, log com ID de correlação.
- Frequência e severidade: acontece sempre ou às vezes? Bloqueia o fluxo?
Logs gigantes em uma linha só atrapalham; o formatador de log deixa tudo legível. Se o problema envolve autenticação, o decodificador de JWT mostra rapidamente claims e expiração do token, mas lembre de nunca colar tokens reais de produção em tickets.
Ambiente reproduzível
“Na minha máquina funciona” é o sintoma de um ambiente que ninguém controla. Para dev e QA enxergarem o mesmo comportamento, vale investir em:
- Infra como código local: containers e um arquivo de composição que sobe aplicação, banco e dependências com um comando.
- Versões fixadas: runtime, dependências e imagens com versão explícita, não “latest”.
- Massa de dados versionada: scripts de seed que criam sempre os mesmos usuários e cenários.
- Ambientes efêmeros por PR, quando possível, para o QA testar a mudança isolada antes do merge.
- Feature flags documentadas: o PR diz qual flag ligar para ver a mudança.
Se você quer treinar tudo isso sem montar nada, o Lab do AppsQuality oferece uma loja de prática em vários fronts e a API de prática tem usuários fixos como standard_user e locked_user, ótimos para escrever critérios de aceite e reproduzir cenários.
Próximos passos
Escolha uma prática para começar na próxima sprint: talvez o template de PR, talvez os critérios em Dado/Quando/Então no refinamento. Para colocar em prática:
- Escreva cenários Dado/Quando/Então para a loja do Lab e automatize alguns deles.
- Teste contratos e mensagens de erro na API de prática.
- Gere dados de borda com o gerador de massa de testes e o gerador de UUID.
- Valide padrões de entrada com o testador de regex.
- Compare saídas esperadas e obtidas com o comparador de textos.
