1 pontos por GN⁺ 2024-02-25 | 1 comentários | Compartilhar no WhatsApp

O novo gerador de testes baseado em LLM da Meta oferece uma visão do futuro do desenvolvimento

  • A Meta publicou o artigo "Automated Unit Test Improvement using Large Language Models at Meta".
  • O artigo mostra como usar IA para acelerar o desenvolvimento e reduzir bugs de software.
  • Ao integrar LLMs ao fluxo de trabalho dos desenvolvedores, ele propõe melhorias de software precisas e completas para aprimorar a cobertura de código atual.

Pontos principais

  • O TestGen-LLM usa a abordagem 'Assured LLM-based Software Engineering' (Assured LLMSE).
  • Ele usa uma abordagem de ensemble que gera melhorias de código com vários LLMs, prompts e hiperparâmetros, e seleciona a melhor melhoria.
  • O TestGen-LLM foi projetado especificamente para melhorar testes já escritos por humanos.

Estatísticas

  • Na avaliação dos produtos Reels e Stories do Instagram, 75% dos casos de teste gerados pelo TestGen-LLM compilaram com sucesso, 57% passaram de forma confiável e 25% aumentaram a cobertura.
  • O TestGen-LLM conseguiu melhorar 10% de todas as classes às quais foi aplicado, e os desenvolvedores aceitaram 73% das melhorias de teste e as colocaram em produção.
  • Em um "test-a-thon" em que engenheiros da Meta geraram testes para aumentar a cobertura de testes do Instagram, a mediana das linhas de código adicionadas pelos testes do TestGen-LLM foi de 2,5.

Insights práticos

  • Este é um bom exemplo de como LLMs podem ser usados para melhorar com eficiência a produtividade de desenvolvimento e a confiabilidade de software.
  • O verdadeiro valor dos LLMs está em encontrar e capturar edge cases inesperados.
  • Para usar LLMs em produção, são necessários orquestração, pipelines e processamento.

Como o TestGen-LLM funciona

  • O TestGen-LLM aplica uma série de filtros semânticos às soluções candidatas geradas pelo LLM interno da Meta para preservar apenas os testes de maior valor.
  • Filtro 1: capacidade de compilação, Filtro 2: execução (se o teste passa), Filtro 3: instabilidade, Filtro 4: melhoria de cobertura.
  • Esses filtros de processamento garantem a melhoria da suíte de testes.

Conclusão

  • Este artigo é uma boa forma de acompanhar os avanços dos LLMs na área de confiabilidade de software, algo para o qual muitos desenvolvedores já usam LLMs.
  • Os LLMs serão cada vez mais capazes de encontrar bugs e testar sistemas de software cada vez mais complexos.

Opinião do GN⁺

  • Este artigo oferece insights interessantes sobre como a inteligência artificial pode impactar o futuro do desenvolvimento de software.
  • Ferramentas como o TestGen-LLM podem ser de grande ajuda para automatizar o trabalho dos desenvolvedores e aumentar a eficiência.
  • O avanço dessas tecnologias está seguindo na direção de reduzir a complexidade do desenvolvimento de software, melhorar a qualidade e economizar tempo dos desenvolvedores.

1 comentários

 
GN⁺ 2024-02-25
Opiniões do Hacker News
  • É interessante a tendência de querer usar LLMs primeiro para escrever código de teste, antes de implementação
    Talvez por eu ter feito TDD demais, vejo testes como algo que descreve como o sistema deve se comportar, e essa parte deveria ser definida por pessoas. O código deve se ajustar dentro dos guardrails criados pelos testes
    Dito isso, LLMs podem ajudar a apontar áreas em que a especificação está insuficiente. Talvez o que esteja acontecendo aqui seja fazê-los sugerir testes unitários para partes menos especificadas
    Mesmo antes dos LLMs, às vezes eu pensava se, caso todos os testes estivessem escritos, um macaco com uma máquina de escrever não conseguiria criar a aplicação

    • Em bases de código legadas, é comum criar testes de caracterização (characterisation tests)
      São testes que definem não como uma pessoa acredita que o código deveria se comportar, mas como a base de código atual realmente se comporta
      Isso permite reescrever, refatorar e redesenhar minimizando regressões. O problema de muito código legado é que ninguém entende o comportamento pretendido e, às vezes, até os usuários acreditam que ele deveria se comportar de um jeito diferente do comportamento real
      Por isso, a coisa mais importante é não mudar o comportamento, a menos que seja uma mudança explicitamente desejada
    • Talvez você já conheça, mas vale lembrar de testes baseados em propriedades (property-based testing). O Hypothesis provavelmente foi o que mais popularizou isso e é recomendável, mas não é a única abordagem nem a única implementação de alta qualidade. Houve uma época em que o QuickCheck do Haskell também ficou grande o suficiente para aparecer no HN
      A ideia básica é expressar proposições mais fracas sobre o comportamento do código como “propriedades”, em vez de um sistema de prova completo em forma fechada, e verificá-las dentro de limites essencialmente probabilísticos
      O exemplo clássico é inverter strings. Se você inverter uma string duas vezes, em geral deveria obter a entrada original. Com uma linha de código, dá para verificar tantos casos extremos estranhos de Unicode quanto o tempo e a eletricidade permitirem
      O exemplo parece trivial, mas vi especialistas em CUDA que trabalhavam com diferenciação automática e kernels — no que virou o PyTorch — usarem essa abordagem de forma extremamente eficaz, conseguindo algo como 5 vezes mais confiança no código com metade do esforço e do custo
      Nem sempre encaixa bem, mas quando encaixa é excelente, e LLMs parecem conseguir chegar bem perto de exemplos de Hypothesis em vez de começar do zero
    • Escrever código de implementação é muito mais divertido e interessante, porque é criar o que a aplicação realmente deve fazer
      Por outro lado, ao escrever testes, você precisa descrever o que a aplicação deve fazer em uma linguagem extremamente verbosa e cheia de restrições, além de escrever dezenas ou centenas de linhas de código de configuração só para inserir alguns if/else glorificados
      Em linguagens como C++ ou Java, testes unitários são feitos de tédio, então não é nada surpreendente surgir o instinto de delegar esse trabalho a um LLM
    • Pode ser porque muitos engenheiros, na prática, não testam bem
      Já vi muitos engenheiros passarem dias codando e depois, por obrigação, escreverem alguns testes para “provar” que o sistema funciona. A cobertura é baixa e geralmente frágil
      Para engenheiros que pensam e trabalham assim, um sistema desses pareceria um presente dos céus
      Já tive um gerente que proibiu escrever testes primeiro porque dizia que era lento. Felizmente eu estava alocado como consultor e pude ignorá-lo dizendo “fale com meu chefe”, mas ele provavelmente pensava como os engenheiros acima
      Visto de outro jeito, a maioria dos desenvolvedores odeia documentação. Eles adorariam uma IA que escrevesse ótima documentação a partir do código. E, para esses desenvolvedores, ótima documentação é documentação que eles não precisam escrever
    • Mesmo fora de IA, tenho uma sensação parecida ao ver como o código de teste é tratado
      Código de teste muitas vezes é tratado como código de baixa prioridade e entregue a engenheiros mais juniores, o que parece exatamente o oposto do desejável
  • Não quero revisar tudo, mas uma parte parece especialmente equivocada
    Li o artigo original por alto logo depois que saiu e agora estou apenas passando os olhos, então parto do pressuposto de que minha memória está meio nebulosa
    O blog escreveu que a maioria dos testes do TestGen-LLM da Meta cobriu apenas mais 2,5 linhas, mas que um teste cobriu 1326 linhas, e que o valor desse único teste era “exponencialmente maior”, além de dizer que havia grande valor no LLM pensar ativamente fora da caixa para capturar casos de borda inesperados
    Mas a expressão “exponencialmente mais valioso” por si só já deveria acionar o detector de bobagem. Ao olhar o paper, os autores descrevem essa cobertura de 1326 linhas como um único teste que acertou o jackpot, e dizem que a cobertura adicional realista esperada por um teste individual do TestGen-LLM tem mediana de 2,5 linhas
    Os autores não mencionam “casos de borda inesperados” nem “pensar fora da caixa”. Pelo contrário, apresentam isso como um caso excepcional que pode ter tocado um ramo de um switch horrível, ou simplesmente ser uma coincidência da forma como a cobertura de código é calculada
    Também chama atenção que eles não aprofundem isso na seção de “resultados qualitativos”. Uma explicação imprecisa não ajuda ninguém. A internet já tem gente demais fingindo entender o que apenas fingiu ter lido

    • Sou o autor do texto, e não quis dizer que os autores do paper falaram em “casos de borda inesperados” ou “pensar fora da caixa”
      Editei o texto para deixar mais claro que algumas interpretações são minha opinião
      O texto é mais um comentário sobre o que os resultados do paper significam do que um resumo do paper. De todo modo, o Hacker News é um lugar para discussão
      Ainda assim, continuo achando correta a parte de “exponencialmente mais valioso”. O ponto central do valor é justamente que um LLM pode, por acaso, “acertar o jackpot” em termos de cobertura de testes
      Se ele continuar tentando várias combinações e acertar um único jackpot como no paper, isso é muito valioso para a equipe. Pode ser um teste que não era óbvio para uma pessoa escrever manualmente, ou que era tedioso demais
      Como alguém que, em bases de código de Big Tech (F/G), já sabia o que precisava ser testado, mas gastou tempo demais tentando descobrir “como testar”, vejo muito valor nisso
    • Os incentivos de produção de código na Meta são, de modo geral, equivocados
      Está claro que essa equipe também está sendo empurrada para métricas centradas em número de linhas de código e quantidade de diffs. No fim, isso será apenas mais uma ferramenta de geração de código que cria uma montanha de código difícil de depurar
  • Bons testes são difíceis, e cobertura não é necessariamente algo bom
    É fácil escrever testes demais, engessar o programa e, na prática, criar um programa de detecção de mudanças. Vira algo como: “Você mudou alguma coisa, todos os testes quebraram. Tudo bem, agora é só pedir para o LLM gerar de novo! 100% de cobertura! Incrível! Progresso!”

    • Concordo. Bons testes são mais de uma ordem de grandeza mais difíceis do que bom código
    • Acho interessante essa perspectiva de “programa de detecção de mudanças”. Fico curioso sobre por que isso é ruim
      Para mim, é uma oportunidade de confirmar se aquela mudança foi intencional. Sem isso, como saber se o programa está fazendo o que deveria fazer?
    • Dá para saber com certeza que código sem cobertura tem testes péssimos
      Fora isso, é preciso ler os bons testes em que outras cinco pessoas pensaram. Todos nós escrevemos testes mal, cada um à sua maneira
    • Em um lugar onde trabalhei havia testes de componentes web que commitavam snapshots do DOM esperado e verificavam se o componente cuspia aquilo
      Depois disso, em toda mudança, o desenvolvedor naturalmente apertava o botão de regenerar e commitava tudo. O diff era rico, mas o sinal era duvidoso
    • O ponto central está nos casos de cauda longa
  • Depois de trabalhar na indústria de semicondutores, especialmente em litografia computacional, onde design orientado a testes é padrão, acho isso difícil de aceitar
    Não quero dizer que testes devam sempre ser escritos antes do código de produção. Mas testes são parte do código tanto quanto qualquer outra parte do codebase, e precisam ser escritos junto com o código que testam
    A parte mais importante dos testes é mostrar a intenção do desenvolvedor. A suíte de testes mostra como o código deve ser usado, o que ele faz, o que não faz e para que foi escrito
    Assim, quando outro desenvolvedor usar ou modificar esse código, não precisa sair procurando pistas pelo codebase como Sherlock Holmes
    Se os testes não contam uma história, você está escrevendo testes do jeito errado
    Até que computadores consigam ler mentes e entender melhor a intenção, geradores baseados em IA/LLM não conseguem fazer esse trabalho no nosso lugar
    Claro, se o único objetivo da suíte de testes for ganhar um check verde na verificação pré-commit e mostrar números bonitos de cobertura, dá para dobrar a produtividade com IA
    Geradores automáticos de código vão ajudar a escrever mais código ruim na velocidade da luz. Se alguém reclamar que o código está inchado e difícil de entender por causa de muito boilerplate, é só dizer para resolver com IA. Funcionou para você
    Esse realmente parece ser o futuro do desenvolvimento, mas não é o futuro que eu espero

    • Concordo com quase tudo, mas acho que também há espaço para esse tipo de teste
      O que você descreveu parece testar o “núcleo” do código. São testes que também servem em parte como documentação, validação e estabilidade
      Outros testes, como fuzzing, entregam um valor completamente diferente. Vejo testes baseados em IA ocupando um espaço voltado para a cauda da distribuição: muitos testes de menor valor que ficam abandonados porque faltam energia e tempo humanos
      Também vejo o estado atual das ferramentas de IA assim: ferramentas de apoio cognitivo
      Eu ficaria até surpreso se essa linha de pesquisa não desse frutos consideráveis nos próximos anos
  • Estou citando de novo, com pequenas revisões, algo que escrevi quando o próprio artigo foi publicado. O texto deles apresenta as estatísticas de forma incorreta
    https://news.ycombinator.com/item?id=39406726
    O resumo não corresponde ao conteúdo real do artigo. Ele faz parecer que os números são proporções por caso de teste, como “75% compilaram corretamente, 57% passaram de forma estável, 25% aumentaram a cobertura”
    O relatório real fala em termos de classes de teste, e cada classe contém um ou mais casos de teste
    O sentido é: “75% das classes de teste tinham pelo menos um novo caso de teste que compilava corretamente”, “57% das classes de teste tinham pelo menos um caso de teste que compilava corretamente e passava de forma estável”, “25% das classes de teste tinham pelo menos um caso de teste que compilava, passava e aumentava a cobertura de linhas em comparação com outras classes de teste do mesmo alvo de build”
    São frases completamente diferentes. Há até uma nota de rodapé dizendo que, como cada tentativa de expandir uma classe de teste pode envolver várias tentativas de geração de casos de teste, a taxa de sucesso por caso de teste geralmente é muito menor do que a taxa por classe de teste
    Mesmo assim, na conclusão eles voltam a apresentar os resultados de forma incorreta, como no resumo. Escrevem que, usando o TestGen-LLM em modo experimental, a taxa de sucesso por caso de teste foi de 25%, e que, ao relaxar a exigência de cobertura de linhas e exigir apenas build e aprovação, a taxa de sucesso sobe para 57%

  • Tenho empatia por quem vai ter que manter esse péssimo código legado de LLM no futuro
    Vai ser feio

    • Obviamente vão usar um LLM para manter isso
    • Então os LLMs não estão eliminando empregos, mas criando-os. Só que não devem ser empregos muito divertidos
    • Acho que não vai ser pior do que código corporativo
      Talvez até pareça bem parecido, com comentários e documentação mais completos, e talvez menos propenso a estar ativamente errado
    • Apagar os testes resolve o problema. O dashboard de CI também mostra checks verdes
    • Concordo
      LLMs nunca vão ficar melhores do que são agora e não evoluíram nada nos últimos 2 anos. São apenas cadeias de Markov chamativas
      Só dá para usá-los para escrever código quando alguém que não sabe programar commita código cegamente em produção sem nenhuma revisão
      Para quem sabe programar, não têm como ser úteis e não aumentam produtividade
      Vou ignorar essa baboseira de LLM que não muda absolutamente nada no mundo, e você certamente deveria fazer o mesmo
  • Se não for bem separado, vira facilmente um ambiente hostil aos desenvolvedores, com gerentes ignorantes exigindo alta cobertura e juniors entusiasmados inserindo escondido grandes volumes de testes por IA
    No fim, toda vez que você envia um trabalho, acaba tendo que receber o carimbo de aprovação de código de teste gerado por LLM que é difícil de manter
    Escrever alguns testes pode ficar mais rápido, mas não há garantia de que a manutenção ficará mais rápida. O mesmo vale para a manutenção do código sendo testado, porque não há garantia de que bons testes serão gerados
    O processo trabalhoso de escrever testes normalmente também ajuda o desenvolvedor a verificar o design logo no início. Se é difícil de testar, geralmente não é um bom design; por exemplo, no contexto de escrever código junto com outras pessoas, muitas vezes o contrato do componente não está suficientemente abstraído
    Um ponto fácil de ignorar é que testes são código sacrificável. A maioria nunca vai capturar nada durante toda a vida, e isso é OK. Eles dão tranquilidade automatizada e, em caso de falha, reduzem as pistas falsas
    Mas investir ao máximo em uma salvaguarda probabilística nem sempre traz retorno. Quanto mais a cobertura se aproxima do topo, menor é a utilidade marginal. A menos que sejam caminhos de execução de muito tráfego, como em uma biblioteca padrão, ostentar cobertura alta geralmente não é recompensado
    Além disso, quase sempre é preciso haver um ecossistema de testes — não só testes unitários, mas testes de integração, testes de sistema etc. — para o conjunto funcionar. Será que um LLM vai sentar na reunião de design, entender a arquitetura e escrever esses testes também? Ou vai exagerar o que consegue fazer e empurrar para fora o que deveria ser feito?
    Ao investir esforço em testes, é preciso ter o senso de julgar “o que é relevante” não só no momento da escrita, mas também no momento do design e da manutenção. Humanos fazem isso muito bem; ferramentas de IA, não
    A parte em que um LLM pode economizar tempo são as teclas digitadas por um desenvolvedor experiente que já tem noção do que vale a pena testar e do que não vale. Ao mesmo tempo, ele também pode atrapalhar, fazendo coisas pouco relevantes entrarem sorrateiramente no código — e isso já vem acontecendo
    Não queremos uma economia que produza teclas digitadas. Queremos um conjunto de entradas bem pensadas e altamente relevantes. E esperamos que esta última seja bem separada da primeira, para que com o tempo a utilidade objetiva — ou a falta dela — fique evidente

  • Já fiz isso com o GPT-4
    Mostrei um módulo TypeScript e pedi para gerar testes unitários; ele criou testes funcionais que cobriam não só o caminho feliz, mas também alguns casos de borda

    • Não consigo entender muito bem por que comentários parecidos recebem downvotes
      O ChatGPT supera as expectativas em vários aspectos. Testes parecem ser algo fácil para o nível de capacidade do GPT
      Na semana passada, pedi que escrevesse código Python que percorresse uma AST e criasse um grafo e componentes no React Flow. Não fiz correções; apenas repeti algumas rodadas de feedback no prompt, e funcionou muito bem. Já vi muitas capacidades interessantes semelhantes no GPT
  • Como a IA sabe quais testes deve escrever?
    É um experimento interessante, mas um tanto suspeito. Acho que a melhor forma de a IA ajudar no desenvolvimento de software é quando o programador faz perguntas sobre seu próprio código ou o código de outras pessoas, e a IA responde. Às vezes isso incluirá sugestões de código, mas não precisa ser sempre assim
    Ela deveria ser capaz de responder a perguntas como “há uma forma de simplificar este código?” ou “quais entradas podem causar erros?”
    A IA deve nos ajudar a entender o código e a entender como melhorá-lo. Se não dissermos o que queremos que ela faça, a IA não pode saber o que queremos, então não devemos deixá-la escrever tudo sozinha
    Testes são um bom exemplo. O que queremos testar?

  • Finalmente apareceu uma geração de código por IA que faz sentido