Pular para o conteúdo

Como testar seu código (mesmo quando a IA o escreve)

Ver no idioma original →

Autoria: ByteByteGo · Assistir no YouTube

Vídeo de autoria do canal indicado, não da IANIA; todos os direitos pertencem ao criador. Transcrição, resumo e tradução gerados automaticamente; o conteúdo no idioma original é preservado na versão em idioma original.

Idioma original: inglês

Vídeo original: How to Test Your Code (Even When AI Writes It) · Canal: ByteByteGo · 04:08 · Assistir no YouTube

Tópicos: Agentes de IA, Engenharia de software com IA

O vídeo explica as três camadas de testes de software: unitários, de integração e end-to-end, mostrando o que cada uma detecta e quais são seus custos. A pirâmide de testes recomenda muitos testes unitários rápidos, menos testes de integração e um conjunto menor de testes end-to-end para os fluxos mais importantes. Quando a IA escreve o código e os testes a partir da mesma implementação, ambos podem repetir a mesma premissa incorreta. Por isso, o comportamento esperado deve vir de uma especificação, de um contrato de API ou de critérios de aceitação, e a IA não pode definir sozinha o que significa estar correto.

  • Testes unitários verificam pequenas partes da lógica isoladamente e costumam ser rápidos.
  • Testes de integração verificam se diferentes componentes, como serviços e banco de dados, funcionam juntos.
  • Testes end-to-end percorrem o fluxo completo do usuário, mas são mais lentos e difíceis de depurar.
  • A pirâmide de testes recomenda muitos testes unitários, menos testes de integração e poucos testes end-to-end.
  • O teste deve ser feito na camada mais baixa que ofereça a confiança necessária.
  • Um agent pode reproduzir nos testes a mesma premissa incorreta usada para escrever a implementação.
  • O comportamento esperado deve ser definido por uma especificação, um contrato de API ou critérios de aceitação.
  • Uma etapa de revisão separada ou um agent diferente pode ajudar a verificar mudanças importantes.
Início Capítulo Resumo
00:00 Testes unitários e de integração O vídeo apresenta testes unitários e de integração por meio de exemplos de cálculo de pedidos e do fluxo de checkout.
01:03 Integração e revisão de código A integração pode falhar por problemas entre serviços, e o vídeo apresenta a interface de revisão do Code Rabbit.
02:05 Testes end-to-end e a pirâmide de testes Testes end-to-end verificam o fluxo completo do usuário, enquanto a pirâmide orienta como equilibrar cobertura, velocidade e facilidade de depuração.
03:08 Testes quando a IA escreve o código O vídeo alerta que código e testes gerados a partir da mesma premissa podem concordar com um erro e destaca a necessidade de definir o comportamento esperado externamente.

Um programa pode passar em todos os testes unitários e, ainda assim, o produto estar quebrado. — 00:00

Um teste end-to-end verifica se o fluxo completo do usuário funciona. — 02:05

A ideia é testar algo na camada mais baixa que nos dê a confiança necessária. — 02:36

O código está errado, os testes concordam com o código e tudo passa. — 03:08

A IA pode tornar esses ciclos de feedback muito mais baratos, mas não define o que significa estar correto. — 04:08

  • unit test (conceito): Verifica uma pequena unidade de lógica isoladamente, sem depender de banco de dados ou de outros serviços.
  • Integration test (conceito): Verifica se múltiplas partes de um sistema funcionam corretamente em conjunto.
  • End-to-end test (conceito): Testa um fluxo completo de uso, da interação do usuário às partes de backend envolvidas.
  • Testing pyramid (conceito): Modelo que recomenda muitos testes unitários, menos testes de integração e um conjunto menor de testes end-to-end.
  • CodeRabbit (ferramenta): Aplicativo de IA para revisão de código apresentado no vídeo como patrocinador.
  • API contract (conceito): Definição do formato e do comportamento esperados na comunicação entre sistemas.
  • Acceptance criteria (conceito): Conjunto de condições que determina se uma funcionalidade atende ao comportamento esperado.

00:00 Um programa pode passar em todos os testes unitários, e ainda assim o produto pode estar com problemas. Talvez o custo do banco de dados esteja errado. Talvez dois serviços discordem sobre uma API. Ou talvez tudo funcione individualmente, mas o fluxo de checkout ainda falhe para o usuário. Isso acontece porque testes diferentes detectam tipos diferentes de bugs. Então, vamos analisar as três camadas de testes de software e para que cada uma serve. Vamos começar pelos testes unitários. Neste exemplo, estamos construindo uma loja virtual. Temos uma função que calcula o preço final de um pedido, incluindo descontos, impostos e frete. Um teste unitário

00:32 pode verificar se um desconto de 10% é calculado corretamente ou se um pedido acima de US$ 50 recebe frete grátis. Estamos testando uma pequena parte da lógica isoladamente, sem banco de dados nem outros serviços. É por isso que os testes unitários costumam ser rápidos, e podemos executar muitos deles sempre que o código muda. Mas testar partes isoladamente não é suficiente. Nosso serviço de checkout, por exemplo, precisa salvar o pedido em um banco de dados. Também precisa chamar o serviço de pagamento. É aí que entram os testes de integração. Um

01:03 teste de integração verifica se várias partes do sistema realmente funcionam juntas. Podemos chamar a API de checkout e verificar se ela grava os dados corretos no banco de dados. Ou podemos verificar se o checkout envia ao serviço de pagamento os campos que ele espera. A lógica do checkout poderia passar em todos os testes unitários e ainda assim falhar aqui, porque o SQL está errado ou porque os dois serviços discordam sobre o formato de uma API. O vídeo de hoje é patrocinado pelo Code Rabbit, o app de IA mais instalado no GitHub e no GitLab. Hoje em dia, um pull request gerado por IA

01:35 pode envolver uma dúzia de coisas sem relação entre si, e revisá-lo significa ficar alternando entre abas. A nova interface de revisão do Code Rabbit resolve isso. Ela divide as diferenças em pequenos trechos relacionados, que você pode analisar um de cada vez. Clique em qualquer função, e a definição aparece ali mesmo. Tem alguma dúvida sobre a mudança? É só perguntar, e você recebe a resposta sem sair da página. E a ferramenta sinaliza primeiro os pontos críticos, para você reduzir pela metade o tempo de revisão e os bugs. Comece um teste de 14 dias; o link está na descrição. Agora, compare isso com um teste de ponta a ponta. Em vez de chamar

02:05 a API de checkout diretamente, abrimos o site como um cliente faria. Adicionamos algo ao carrinho, passamos pelo checkout e verificamos se a página de confirmação aparece. Agora estamos testando o caminho inteiro: frontend, backend, banco de dados e os serviços por trás deles. Essa é a principal diferença. Um teste de ponta a ponta verifica se o fluxo completo do usuário funciona. Quanto mais abrangente o teste, maior é a parte do sistema real que ele exercita. Mas também fica mais difícil de configurar, mais lento para executar e mais difícil de depurar. Se um

02:36 teste unitário falhar, geralmente há pouco código a investigar. Se um teste de ponta a ponta falhar, o problema pode estar em quase qualquer ponto do caminho. É daí que vem a ideia de uma pirâmide de testes. Em geral, queremos muitos testes pequenos e rápidos na base, menos testes de integração acima deles e um conjunto menor de testes de ponta a ponta cobrindo os fluxos de trabalho mais importantes. A ideia é testar algo na camada mais baixa que nos dê a confiança necessária. Então, o que muda quando a IA escreve o código? Principalmente, a velocidade

03:08 e o volume das mudanças. Um agent de IA pode fazer uma grande mudança, gerar testes para ela, executar esses testes, corrigir as falhas e tentar novamente. Mas há um modo de falha que precisamos observar. Se o agent escreve a implementação e depois escreve testes com base nessa mesma implementação, pode repetir a mesma suposição errada nas duas coisas. O código está errado, os testes concordam com o código, e tudo passa. O comportamento esperado precisa vir de outro lugar: uma especificação, um contrato de API

03:38 ou um conjunto de critérios de aceitação. Para mudanças importantes, também podemos ter uma etapa de revisão separada ou até um agent separado para verificar o resultado. Não deixe o agent que construiu algo avaliar o próprio trabalho. A mesma pirâmide básica de testes continua valendo. Os testes unitários nos dão feedback rápido. Os testes de integração detectam problemas nas fronteiras entre sistemas. Os testes de ponta a ponta nos dizem se o fluxo de trabalho inteiro realmente funciona. A IA pode tornar muito mais barato criar e usar esses ciclos de feedback, mas

04:08 ela não define o que significa estar correto. Ainda temos que fazer isso.

Transcrição original

[00:00] A program can pass every unit test, and the product can still be broken. Maybe the database cost is wrong. Maybe two services disagree on an API. Or maybe everything works individually, but the checkout flow still fails for the user. That’s because different tests catch different kinds of bugs. So, let’s look at the three layers of software testing and what each one is good for. Let’s start with unit tests. In this example, we’re building an online store. We have a function that calculates the final price of an order, including discounts, taxes, and shipping. A unit

[00:32] test might check whether a 10% discount is calculated correctly, or whether an order over $50 gets free shipping. We’re testing one small piece of logic on its own with no database and no other services. That’s why unit tests tend to be fast, and we can run a lot of them every time the code changes. But testing pieces in isolation isn’t enough. Our checkout service, for example, needs to save the order to a database. It also needs to call the payment service. This is where integration tests come in. An

[01:03] integration test checks whether multiple parts of the system actually work together. We might call the checkout API and verify that it writes the right data to the database. Or we might verify that checkout sends the payment service the fields it expects. The checkout logic could pass every unit test and still fail here because the SQL is wrong, or because the two services disagree on an API format. Today’s video is sponsored by Code Rabbit, the most installed AI app on GitHub and GitLab. These days, one AI-generated pull

[01:35] request can touch a dozen unrelated things, and reviewing it means bouncing between tabs. Code Rabbit’s new review UI fixes that. It splits the diffs into small, related chunks you can take one at a time. Click any function, and its definition pops up right there. Got a question about the change? Just ask, and you get the answer without leaving the page. And it flags the critical stuff first, so you cut review time and bugs in half. Start a 14-day trial, link in the description. Now compare that with an end-to-end test. Instead of calling

[02:05] the checkout API directly, we open the website like a customer would. We add something to the cart, go through checkout, and make sure the confirmation page appears. Now we are testing the whole path, the front end, back end, database, and the services behind them. That’s the key difference. An end-to-end test checks whether the complete user workflow works. The broader the test becomes, the more the real system it exercises. But it also becomes more difficult to set up, slower to run, and harder to debug. If a

[02:36] unit test fail, there’s usually a small amount of code to investigate. If an end-to-end test fails, the problem could be almost anywhere along the path. This is where the idea of a testing pyramid comes from. We generally want lots of small, fast tests near the bottom, fewer integration tests above them, and a smaller set of end-to-end tests covering the most important workflows. The idea is to test something at the lowest layer that can give us the confidence we need. So what changes when AI writes the code? Mostly, the speed

[03:08] and the volume of change. An AI agent can make a large change, generate tests for it, run those tests, fix the failures, and try again. But there’s one failure mode we need to watch for. If the agent writes the implementation, then writes tests based on the same implementation, it can carry the same wrong assumption into both. The code is wrong, the tests agree with the code, everything passes. The expected behavior has to come from somewhere else, a specification, an API

[03:38] contract, or a set of acceptance criteria. For important changes, we can also have a separate review step, or even a separate agent to verify the result. Don’t let the agent that build it grade its own work. The same basic testing pyramids still applies. Unit tests give us fast feedback. Integration tests catch problems at the boundary between systems. End-to-end tests tell us whether the whole workflow actually works. AI can make it much cheaper to build and use these feedback loops, but

[04:08] it doesn’t define what correct means. We still have to do that.