JavaRush /Blogue Java /Random-PT /O que é TDD e teste unitário [tradução]
Dr-John Zoidberg
Nível 41
Марс

O que é TDD e teste unitário [tradução]

Publicado no grupo Random-PT
Este artigo é uma adaptação de um capítulo do livro The Complete Software Career Guide. Seu autor, John Sonmez, o escreve e publica alguns capítulos em seu site.
O que é TDD e teste unitário [tradução] - 1

Um breve glossário para iniciantes

O teste de unidade ou teste de unidade é um processo de programação que permite verificar a correção de módulos individuais do código-fonte de um programa. A ideia é escrever testes para cada função ou método não trivial. Teste de regressão é um nome geral para todos os tipos de teste de software destinados a detectar erros em áreas já testadas do código-fonte. Tais erros - quando, após fazer alterações no programa, algo que deveria continuar funcionando para de funcionar - são chamados de erros de regressão. Resultado vermelho, falha - falha no teste. A diferença entre o resultado esperado e o real. Resultado verde, aprovado - resultado de teste positivo. O resultado real não é diferente do que foi obtido. ***
O que é TDD e teste unitário [tradução] - 2
Tenho um relacionamento muito misto com Test Driven Development (TDD) e testes unitários, indo do amor ao ódio e vice-versa. Eu era um fã ávido e ao mesmo tempo um cético desconfiado sobre o uso desta e de outras “melhores práticas”. A razão da minha atitude baseia-se no facto de ter surgido um grave problema nos processos de desenvolvimento de software: os programadores, e por vezes gestores, utilizam determinadas ferramentas e metodologias apenas porque pertencem às “melhores práticas”. A verdadeira razão para a sua utilização permanece obscura. Um dia comecei a trabalhar em um determinado projeto e, no processo, fui informado que estaríamos modificando o código coberto por um grande número de testes unitários. Não é brincadeira, eram cerca de 3.000. Isso geralmente é um bom sinal, um sinal de que os desenvolvedores estão usando metodologias avançadas. O código com essa abordagem geralmente é estruturado e baseado em uma arquitetura bem pensada. Em suma, a presença dos testes me deixou feliz, até porque significou facilitar meu trabalho como mentor de programadores. Como já tínhamos testes unitários, bastava conectar a equipe de desenvolvimento para apoiá-los e começar a escrever nosso próprio código. Abri o IDE (ambiente de desenvolvimento integrado) e carreguei o projeto.
O que é TDD e teste unitário [tradução] - 3
Foi um grande projeto! Encontrei uma pasta chamada "testes unitários". “Ótimo”, pensei. - Vamos iniciá-lo e ver o que acontece. Demorou apenas alguns minutos e, para minha surpresa, todos os testes foram aprovados, tudo ficou verde ( "verde" é um resultado positivo do teste. Sinaliza que o código está funcionando conforme o esperado. Vermelho indica "falha" ou falha, então há um caso em que o código não funciona corretamente - nota do tradutor ). Todos passaram no teste. Naquele momento, o cético em mim acordou. Como é que, três mil testes unitários, eles fizeram todos de uma vez - e deram resultado positivo? Em minha longa prática, não conseguia me lembrar de uma época em que comecei a trabalhar em um projeto sem um único teste de unidade negativo no código. O que fazer? Verifique manualmente! ChY escolheu um teste aleatório, não o mais revelador, mas ficou imediatamente claro o que ele estava verificando. Mas ao trabalhar nisso, percebi algo absurdo: o teste não continha nenhuma comparação com o resultado esperado (afirmações)! Ou seja, na realidade nada foi verificado ! Houve certas etapas do teste, elas foram realizadas, mas ao final do teste, onde ele deveria comparar o resultado real e o esperado, não houve verificação. O "teste" não testou nada. Abri outro teste. Melhor ainda: o operador de comparação com o resultado foi comentado. Brilhantemente! Esta é uma ótima maneira de passar no teste, basta comentar o código que está causando a falha. Fiz outro teste, depois outro... Nenhum deles verificou nada. Três mil testes, e todos eles são completamente inúteis. Há uma enorme diferença entre escrever testes unitários e entender testes unitários e desenvolvimento orientado a testes (TDD).

O que é teste unitário?

O que é TDD e teste unitário [tradução] - 4
A ideia básica do teste unitário é escrever testes que testem a menor “unidade” de código. Os testes de unidade geralmente são escritos na mesma linguagem de programação do código-fonte do aplicativo. Eles são criados diretamente para testar este código. Ou seja, testes unitários são códigos que verificam a exatidão de outro código. Eu uso a palavra “teste” de forma bastante liberal no contexto, porque os testes unitários, em certo sentido, não são testes. Eles não experimentam nada. O que quero dizer é que quando você executa um teste de unidade você normalmente não descobre que algum código não funciona. Você descobre isso enquanto escreve o teste, porque alterará o código até que o teste fique verde. Sim, o código pode mudar posteriormente e seu teste pode falhar. Portanto, nesse sentido, um teste unitário é um teste de regressão. Um teste de unidade não é como um teste normal, onde você segue alguns passos e verifica se o software funciona corretamente ou não. Durante o processo de escrever um teste de unidade, você descobre se o código faz o que deveria ou não e irá alterá-lo até que o teste seja aprovado.
O que é TDD e testes unitários [tradução] - 5
Por que não escrever um teste de unidade e verificar se ele passa? Se você pensar dessa maneira, os testes de unidade se transformarão em algum tipo de requisito absoluto para determinados módulos de código em um nível muito baixo. Você pode pensar em um teste unitário como uma especificação absoluta . Um teste de unidade determina que sob essas condições, com esse conjunto específico de entradas, existe uma saída que você deve obter dessa unidade de código. O verdadeiro teste de unidade identifica a menor unidade de código coerente, que na maioria das linguagens de programação - pelo menos nas orientadas a objetos - é uma classe.

O que às vezes é chamado de teste unitário?

O que é TDD e testes unitários [tradução] - 6
O teste de unidade é frequentemente confundido com o teste de integração. Alguns "testes unitários" testam mais de uma classe ou testam grandes unidades de código. Muitos desenvolvedores afirmam que escrevem testes de unidade quando na verdade escrevem testes de caixa branca de baixo nível. Não discuta com esses caras. Apenas saiba que eles realmente escrevem testes de integração, e os verdadeiros testes unitários testam a menor unidade de código isoladamente de outras partes. Outra coisa que costuma ser chamada de teste unitário são os testes unitários sem verificação em relação a um valor esperado. Em outras palavras, testes unitários que na verdade não testam nada. Qualquer teste, unitizado ou não, deve incluir algum tipo de verificação – chamamos isso de verificação do resultado real em relação ao resultado esperado. É essa reconciliação que determina se o teste será aprovado ou reprovado. Um teste que sempre passa é inútil. Um teste que sempre falha é inútil.

O valor do teste unitário

Por que sou um entusiasta de testes unitários? Por que é prejudicial chamar o teste generalizado, que envolve testar não o menor bloco isolado de outro código, mas um pedaço maior de código, de “teste unitário”? Qual é o problema se alguns dos meus testes não compararem os resultados recebidos e esperados? Pelo menos eles executam o código. Vou tentar explicar.
O que é TDD e testes unitários [tradução] - 7
Existem duas razões principais para realizar testes unitários. A primeira é melhorar o design do código. Lembra como eu disse que testes unitários não são realmente testes? Ao escrever testes de unidade adequados, você se força a isolar a menor unidade de código. Essas tentativas levarão você a descobrir problemas na estrutura do próprio código. Você pode achar muito difícil isolar a classe de teste e não incluir suas dependências, e isso pode fazer você perceber que seu código está muito acoplado. Você pode descobrir que a funcionalidade principal que está tentando testar se estende por vários módulos, levando você a acreditar que seu código não é coerente o suficiente. Quando você se senta para escrever um teste de unidade, você pode descobrir de repente (e acredite, isso acontece!) que você não tem ideia do que o código deve fazer. Conseqüentemente, você não poderá escrever um teste de unidade para ele. E, claro, você pode encontrar um bug real na implementação do código, já que o teste unitário força você a pensar fora da caixa e testar diferentes conjuntos de entradas que você talvez não tenha considerado.
O que é TDD e testes unitários [tradução] - 8
Se você seguir estritamente a regra de “testar a menor unidade de código isolada das outras” ao criar testes de unidade, certamente encontrará todos os tipos de problemas com esse código e com o design desses módulos. No ciclo de vida de desenvolvimento de software, o teste unitário é mais uma atividade de avaliação do que uma atividade de teste. O segundo objetivo principal do teste unitário é criar um conjunto automatizado de testes de regressão que possa atuar como uma especificação de baixo nível do comportamento do software. O que isso significa? Quando você amassa a massa, você não a quebra. Deste ponto de vista, os testes unitários são testes, mais especificamente testes de regressão. No entanto, o objetivo dos testes unitários não é simplesmente construir testes de regressão. Na prática, os testes unitários raramente detectam regressões, já que uma alteração na unidade de código que você está testando quase sempre contém alterações no próprio teste unitário. O teste de regressão é muito mais eficaz em um nível superior, quando o código é testado como uma “caixa preta”, porque nesse nível a estrutura interna do código pode ser alterada, enquanto se espera que o comportamento externo permaneça o mesmo. Os testes unitários, por sua vez, verificam a estrutura interna para que, quando essa estrutura mudar, os testes unitários não falhem. Eles se tornam inutilizáveis ​​e agora precisam ser alterados, jogados fora ou reescritos. Agora você sabe mais sobre o verdadeiro propósito dos testes unitários do que muitos desenvolvedores de software veteranos.

O que é Desenvolvimento Orientado a Testes (TDD)?

O que é TDD e testes unitários [tradução] - 9
No processo de desenvolvimento de software, uma boa especificação vale seu peso em ouro. A abordagem TDD é que antes de escrever qualquer código, você primeiro escreve um teste que servirá como especificação, ou seja, define o que o código deve fazer. Este é um conceito de desenvolvimento de software extremamente poderoso, mas muitas vezes é mal utilizado. Normalmente, o desenvolvimento orientado a testes significa usar testes unitários para orientar a criação do código do aplicativo. Mas, na verdade, esta abordagem pode ser aplicada a qualquer nível. No entanto, neste artigo assumiremos que estamos usando testes unitários para nossa aplicação. A abordagem TDD vira tudo de cabeça para baixo e, em vez de escrever o código primeiro e depois escrever testes de unidade para testar esse código, você escreve primeiro um teste de unidade e depois escreve o código para tornar esse teste verde. Dessa forma, o teste de unidade “impulsiona” o desenvolvimento do código. Este processo é repetido continuamente. Você escreve outro teste que define mais funcionalidades do que o código deve fazer. Em seguida, você escreve e modifica o código para garantir que o teste seja concluído com êxito. Depois de obter um resultado verde, você começa a refatorar o código, ou seja, refatorá-lo ou limpá-lo para torná-lo mais conciso. Essa cadeia de processos costuma ser chamada de "Refatoração Vermelho-Verde" porque primeiro o teste de unidade falha (vermelho), depois o código é escrito para se adaptar ao teste, garantindo que seja bem-sucedido (verde) e, finalmente, o código é otimizado ( reestruturação). .

Qual é o objetivo do TDD?

O que é TDD e testes unitários [tradução] - 10
O desenvolvimento orientado a testes (TDD), assim como o teste de unidade, pode ser usado incorretamente. É muito fácil chamar o que você faz de “TDD” e até mesmo seguir a prática sem entender por que está fazendo dessa maneira. O maior valor do TDD é que os testes são realizados para produzir especificações de qualidade. TDD é essencialmente a prática de escrever especificações precisas que podem ser verificadas automaticamente antes da escrita da codificação. Os testes são as melhores especificações porque não mentem. Eles não vão te contar depois de duas semanas de tormento com o código “não foi isso que eu quis dizer”. Os testes, quando escritos corretamente, são aprovados ou reprovados. Os testes indicam claramente o que deve acontecer em determinadas circunstâncias. Assim, o objetivo do TDD é nos dar uma compreensão completa do que precisamos implementar antes de começarmos a implementá-lo. Se você está começando com TDD e não consegue descobrir o que o teste deve testar, então você precisa fazer mais perguntas. Outra função importante do TDD é preservar e otimizar o código. A manutenção do código é cara. Costumo brincar que o melhor programador é aquele que escreve o código mais curto que resolve algum problema. Ou mesmo aquele que prova que esse problema não precisa ser resolvido, e assim remove completamente o código, pois foi esse programador quem encontrou o caminho certo para diminuir o número de erros e diminuir o custo de manutenção da aplicação. Ao usar o TDD, você pode ter certeza absoluta de que não está escrevendo nenhum código desnecessário, já que estará escrevendo código apenas para passar nos testes. Existe um princípio de desenvolvimento de software chamado YAGNI (você não vai precisar dele). TDD impede YAGNI.

Fluxo de trabalho típico de desenvolvimento orientado a testes (TDD)

O que é TDD e testes unitários [tradução] - 11
Compreender o significado do TDD de um ponto de vista puramente acadêmico é difícil. Então, vejamos um exemplo de sessão TDD. Imagine sentar-se em sua mesa e esboçar rapidamente o que você acha que será um design de alto nível para um recurso que permite ao usuário fazer login em um aplicativo e alterar sua senha caso a esqueça. Você decide que começará com a primeira implementação da função de login, criando uma classe que irá lidar com toda a lógica do processo de login. Você abre seu editor favorito e cria um teste de unidade chamado "Login vazio impede que o usuário faça login". Você escreve o código de teste de unidade que primeiro cria uma instância da classe Login (que você ainda não criou). Em seguida, você escreve um código para chamar um método na classe Login que passa um nome de usuário e uma senha vazios. Por fim, você verifica o resultado esperado, verificando se o usuário com login vazio realmente não está logado. Você está tentando executar um teste, mas ele nem compila porque você não tem uma classe Login. Você corrige essa situação e cria uma classe Login junto com um método nessa classe para efetuar login e outro para verificar o status do usuário para ver se ele está logado. Até agora você não implementou a funcionalidade desta classe e o método que precisamos. Você executa o teste neste momento. Agora ele compila, mas falha imediatamente.
O que é TDD e testes unitários [tradução] - 12
Agora você volta ao código e implementa a funcionalidade para passar no teste. No nosso caso, isso significa que devemos obter o resultado: “o usuário não está logado”. Você executa o teste novamente e agora ele passa. Vamos para o próximo teste. Agora vamos imaginar que você precisa escrever um teste chamado "O usuário está logado se digitou um nome de usuário e uma senha válidos". Você escreve um teste de unidade que instancia a classe Login e tenta fazer login com um nome de usuário e senha. Em um teste de unidade, você escreve uma declaração de que a classe Login deve responder sim à questão de saber se o usuário está logado. Você executa esse novo teste e claro que ele falha porque sua classe Login sempre retorna que o usuário não está logado. Você retorna à sua classe Login e implementa algum código para verificar se o usuário está logado. Neste caso, você terá que descobrir como isolar este módulo. Por enquanto, a maneira mais fácil de fazer isso é codificar o nome de usuário e a senha que você usou no teste e, se corresponderem, retornar o resultado “o usuário está logado”. Você faz essa alteração, executa os dois testes e ambos passam. Vamos para a última etapa: você olha o código gerado e procura uma forma de reorganizá-lo e simplificá-lo. Portanto, o algoritmo TDD é:
  1. Criou um teste.
  2. Escrevemos o código para este teste.
  3. Refatorou o código.

conclusões

O que é TDD e testes unitários [tradução] - 13
Isso é tudo que eu queria contar sobre testes unitários e TDD neste estágio. Na verdade, existem muitas dificuldades associadas à tentativa de isolar módulos de código, uma vez que o código pode ser muito complexo e confuso. Muito poucas classes existem em completo isolamento. Em vez disso, eles têm dependências, e essas dependências têm dependências, e assim por diante. Para lidar com tais situações, o veterano do TDD usa mocks, que ajudam a isolar classes individuais substituindo objetos em módulos dependentes. Este artigo é apenas uma visão geral e uma introdução um tanto simplificada aos testes unitários e TDD. Não entraremos em detalhes sobre módulos fictícios e outras técnicas de TDD. A ideia é fornecer os conceitos e princípios básicos de TDD e testes unitários que esperamos que você tenha agora. Original - https://simpleprogrammer.com/2017/01/30/tdd-unit-testing/
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION