Por que usar o JUnit para testes?

131

Talvez minha pergunta seja nova, mas eu realmente não consigo entender as circunstâncias em que eu usaria ?

Se eu escrevo aplicativos simples ou aplicativos maiores, testo-os com as System.outinstruções e isso me parece bastante fácil.

Por que criar classes de teste com JUnit, pastas desnecessárias no projeto, se ainda precisamos chamar os mesmos métodos, verificar o que eles retornam e, em seguida, temos uma sobrecarga de anotar tudo?

Por que não escrever uma classe e testá-la imediatamente com, System.outmas não criar, classes de teste?

PS. Eu nunca trabalhei em grandes projetos, estou apenas aprendendo.

Então qual é o propósito?

Artem Moskalev
fonte
7
Você está ciente de que toda vez que você altera algo no seu programa, todo o seu trabalho anterior de examinar manualmente a saída é invalidado e você precisa refazê-las desde o início?
Thorbjørn Ravn Andersen
Não apenas "testes", mas também "testes inteligentes" é muito importante. Aqui está um bom exemplo: wp.me/prMeE-11
akcasoy

Respostas:

139

Isso não está testando, está "olhando manualmente para a saída" (conhecida como LMAO). Mais formalmente, é conhecido como "procurando manualmente por resultados anormais" (LMFAO). (Ver nota abaixo)

Sempre que alterar o código, você deve executar o aplicativo e o LMFAO para todo o código afetado por essas alterações. Mesmo em pequenos projetos, isso é problemático e propenso a erros.

Agora dimensione até 50k, 250k, 1m LOC ou mais e o LMFAO sempre que você fizer uma alteração no código. Não é apenas desagradável, é impossível: você escalou as combinações de entradas, saídas, sinalizadores, condições e é difícil exercer todas as ramificações possíveis.

Pior ainda, LMFAO pode significar visitar páginas e páginas de aplicativos da web, executar relatórios, examinar milhões de linhas de registros em dezenas de arquivos e máquinas, ler e-mails gerados e entregues, verificar mensagens de texto, verificar o caminho de um robô, encher uma garrafa de refrigerante, agregando dados de uma centena de serviços web, verificando a trilha de auditoria de uma transação financeira ... você entendeu. "Saída" não significa algumas linhas de texto, "saída" significa comportamento agregado do sistema.

Por fim, os testes de unidade e comportamento definem o comportamento do sistema. Os testes podem ser executados por um servidor de integração contínuo e verificados quanto à correção. Claro que sim System.out, mas o servidor de IC não saberá se um deles está errado - e, se o fizer, são testes de unidade, e você também pode usar uma estrutura.

Por melhor que pensemos que somos, os humanos não são boas estruturas de teste de unidade ou servidores de CI.


Nota: O LMAO está testando, mas em um sentido muito limitado. Não é repetível de forma significativa em todo o projeto ou como parte de um processo. É semelhante ao desenvolvimento incremental em um REPL, mas nunca formalizando esses testes incrementais.

Dave Newton
fonte
3
-1 para a primeira frase, que é completa e totalmente falsa.
Michael Borgwardt
50

Escrevemos testes para verificar a correção do comportamento de um programa.

Verificar a correção do comportamento de um programa, inspecionando o conteúdo das instruções de saída usando seus olhos, é um processo manual ou, mais especificamente, visual .

Você poderia argumentar que

Se a inspeção visual funcionar , eu verifico se o código faz o que deve ser feito, para esses cenários e, depois de ver que está correto, estamos prontos para prosseguir.

Agora, primeiro, é ótimo que você esteja interessado em saber se o código funciona ou não corretamente. Isso é uma coisa boa. Você está à frente da curva! Infelizmente, há problemas com isso como uma abordagem.

O primeiro problema com a inspeção visual é que você sofreu um grave acidente de soldagem e nunca conseguiu verificar novamente a correção do seu código.

O segundo problema é que o par de olhos usado está fortemente acoplado ao cérebro do dono dos olhos. Se o autor do código também possui os olhos usados ​​no processo de inspeção visual, o processo de verificação da correção depende do conhecimento sobre o programa internalizado no cérebro do inspetor visual.

É difícil para um novo par de olhos entrar e verificar a correção do código simplesmente porque eles não são associados ao cérebro do codificador original. O proprietário do segundo par de olhos terá que conversar com o autor original do código para entender completamente o código em questão. A conversa como meio de compartilhar conhecimento é notoriamente não confiável. Um ponto discutível se o codificador original não estiver disponível para os novos olhos de par. Nesse caso, o novo par de olhos precisa ler o código original.

Ler o código de outras pessoas que não é coberto por testes de unidade é mais difícil do que ler o código que possui testes de unidade associados. Na melhor das hipóteses, ler o código de outras pessoas é um trabalho complicado, na pior das hipóteses, é a tarefa mais incômoda da engenharia de software. Há uma razão pela qual os empregadores, ao anunciar vagas, destacam que um projeto é um greenfield (ou novíssimo). Escrever código do zero é mais fácil do que modificar o código existente e, assim, tornar o trabalho anunciado mais atraente para os funcionários em potencial.

Com o teste de unidade, dividimos o código em suas partes componentes. Para cada componente, organizamos nossa tenda, indicando como o programa deve se comportar . Cada teste de unidade conta uma história de como essa parte do programa deve agir em um cenário específico. Cada teste de unidade é como uma cláusula de um contrato que descreve o que deve acontecer do ponto de vista do código do cliente.

Isso significa que um novo par de olhos possui dois fios de documentação ativa e precisa sobre o código em questão.

Primeiro eles têm o próprio código, a implementação, como o código foi feito ; segundo, eles têm todo o conhecimento que o codificador original descreveu em um conjunto de declarações formais que contam a história de como esse código deve se comportar.

Os testes de unidade capturam e descrevem formalmente o conhecimento que o autor original possuía quando implementou a classe. Eles fornecem uma descrição de como essa classe se comporta quando usada por um cliente.

Você está certo ao questionar a utilidade de fazer isso, porque é possível escrever testes de unidade que são inúteis, não cobrem todo o código em questão, ficam obsoletos ou desatualizados e assim por diante. Como garantir que os testes de unidade não apenas imitem, mas melhorem o processo de um autor experiente e consciente que inspeciona visualmente as instruções de saída de seu código em tempo de execução? Escreva o teste de unidade primeiro e depois o código para fazer esse teste passar. Quando terminar, deixe que os computadores executem os testes, eles são rápidos e são ótimos em realizar tarefas repetitivas, ideais para o trabalho.

Garanta a qualidade do teste, revendo-os sempre que tocar no código que eles testam e executar os testes para cada compilação. Se um teste falhar, corrija-o imediatamente.

Automatizamos o processo de execução de testes para que sejam executados toda vez que fazemos a construção do projeto. Também automatizamos a geração de relatórios de cobertura de código que detalham qual porcentagem de código é coberta e exercida pelos testes. Nós nos esforçamos para obter altas porcentagens. Algumas empresas impedirão que as alterações de código sejam registradas no controle do código-fonte se elas não tiverem testes de unidade suficientes escritos para descrever quaisquer alterações no comportamento do código. Normalmente, um segundo par de olhos revisará as alterações de código em conjunto com o autor das alterações. O revisor passará pelas alterações para garantir que as alterações sejam compreensíveis e suficientemente cobertas pelos testes. Portanto, o processo de revisão é manual, mas quando os testes (testes de unidade e integração e possivelmente testes de aceitação do usuário) passam nesse processo de revisão manual, tornam-se parte do processo de criação automática. Eles são executados toda vez que uma alteração é registrada. O servidor executa essa tarefa como parte do processo de construção.

Os testes executados automaticamente mantêm a integridade do comportamento do código e ajudam a impedir que futuras alterações na base de código quebrem o código .

Por fim, o fornecimento de testes permite re-fatorar agressivamente o código, pois você pode tornar grandes melhorias de código seguras, sabendo que suas alterações não quebram os testes existentes.

Há uma ressalva para o Test Driven Development e é que você precisa escrever um código para torná-lo testável. Isso envolve codificar para interfaces e usar técnicas como injeção de dependência para instanciar objetos de colaboração. Confira o trabalho de Kent Beck, que descreve muito bem o TDD. Procure codificação para interfaces e estude

Rob Kielty
fonte
13

Ao testar usando algo como System.out, você está testando apenas um pequeno subconjunto de possíveis casos de uso. Isso não é muito completo quando você lida com sistemas que podem aceitar uma quantidade quase infinita de entradas diferentes.

Os testes de unidade foram projetados para permitir a execução rápida de testes em seu aplicativo usando um conjunto muito grande e diversificado de entradas de dados diferentes. Além disso, os melhores testes de unidade também são responsáveis ​​por casos de fronteira, como as entradas de dados que ficam bem no limite do que é considerado válido.

Para um ser humano, testar todas essas entradas diferentes pode levar semanas, enquanto que uma máquina pode levar alguns minutos.

Pense assim: você também não está "testando" algo que será estático. Seu aplicativo provavelmente está passando por mudanças constantes. Portanto, esses testes de unidade são projetados para serem executados em diferentes pontos do ciclo de compilação ou implantação. Talvez a maior vantagem seja esta:

Se você quebrar algo em seu código, você saberá sobre isso agora , não depois da implantação, nem quando um testador de controle de qualidade detectar um bug, nem quando seus clientes tiverem cancelado. Você também terá uma chance melhor de corrigir a falha imediatamente , pois fica claro que o que quebrou a parte do código em questão provavelmente aconteceu desde a sua última compilação. Assim, a quantidade de trabalho de investigação necessário para corrigir o problema é bastante reduzida.

jmort253
fonte
9

Eu adicionei alguns outros System.out NÃO podem fazer:

  • Torne cada caso de teste independente (é importante)

    O JUnit pode fazer isso: toda vez que uma nova instância de caso de teste será criada e @Beforechamada.

  • Separe o código de teste da fonte

    JUnit pode fazê-lo.

  • Integração com CI

    O JUnit pode fazer isso com Ant e Maven.

  • Organize e combine casos de teste facilmente

    O JUnit pode fazer @Ignoree testar o conjunto.

  • Resultado fácil de verificar

    JUnit oferece muitos métodos Assert ( assertEquals, assertSame...)

  • Mock e stub fazem você se concentrar no módulo de teste.

    O JUnit pode fazer: O uso de mock e stub faz com que você configure o equipamento correto e se concentre na lógica do módulo de teste.

远 声 远 Shengyuan Lu
fonte
9

Os testes de unidade garantem que o código funcione conforme o esperado. Eles também são muito úteis para garantir que o código ainda funcione conforme o planejado, caso você precise alterá-lo posteriormente para criar novas funcionalidades para corrigir um bug. Ter uma cobertura de teste alta do seu código permite continuar desenvolvendo recursos sem precisar executar muitos testes manuais.

Sua abordagem manual System.outé boa, mas não a melhor. Esse é um teste único que você executa. No mundo real, os requisitos continuam mudando e na maioria das vezes você faz muitas modificações nas funções e classes existentes. Então ... nem toda vez que você testar o trecho de código já escrito.

também existem alguns recursos mais avançados no JUnit, como

Declarações de afirmação

O JUnit fornece métodos para testar determinadas condições. Esses métodos geralmente começam com assertivos e permitem especificar a mensagem de erro, o resultado esperado e o real.

Alguns desses métodos são

  1. fail([message])- Permite que o teste falhe. Pode ser usado para verificar se uma determinada parte do código não é alcançada. Ou ter um teste com falha antes que o código de teste seja implementado.
  2. assertTrue(true)/ assertTrue(false)- Sempre será verdadeiro / falso. Pode ser usado para predefinir um resultado de teste, se o teste ainda não estiver implementado.
  3. assertTrue([message,] condition)- Verifica se o booleano conditioné verdadeiro.
  4. assertEquals([message,] expected, actual)- Testa se dois valores são iguais (de acordo com o equalsmétodo, se implementado, caso contrário, usando ==comparação de referência). Nota: Para matrizes, é a referência que é verificada, e não o conteúdo, usado assertArrayEquals([message,] expected, actual)para isso.
  5. assertEquals([message,] expected, actual, delta)- Testa se dois valores flutuantes ou duplos estão a uma certa distância um do outro, controlados pelo deltavalor.
  6. assertNull([message,] object) - Verifica se o objeto é nulo

e assim por diante. Veja o Javadoc completo para todos os exemplos aqui .

Suítes

Com os conjuntos de testes, você pode, de certo modo, combinar várias classes de teste em uma única unidade, para poder executá-las todas de uma vez. Um exemplo simples, combinando as classes de teste MyClassTeste MySecondClassTestem um Suite chamado AllTests:

import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({ MyClassTest.class, MySecondClassTest.class })
public class AllTests { } 
Arvind
fonte
6

A principal vantagem do JUnit é que ele é automatizado, e você não precisa verificar manualmente com as impressões. Cada teste que você escreve permanece no seu sistema. Isso significa que, se você fizer uma alteração que tenha um efeito colateral inesperado, seu teste será detectado e falhará, em vez de você ter que se lembrar de testar manualmente tudo após cada alteração.

n00begon
fonte
4

JUnit é uma estrutura de teste de unidade para a linguagem de programação Java. É importante no desenvolvimento orientado a teste e faz parte de uma família de estruturas de teste de unidade conhecidas coletivamente como xUnit.

O JUnit promove a idéia de "primeiro teste e depois codificação", com ênfase na configuração dos dados de teste para um trecho de código que pode ser testado primeiro e depois implementado. Essa abordagem é como "teste um pouco, codifique um pouco, teste um pouco, codifique um pouco ...", que aumenta a produtividade do programador e a estabilidade do código do programa que reduz o estresse do programador e o tempo gasto na depuração.

Recursos O JUnit é uma estrutura de código aberto usada para escrever e executar testes.

Fornece anotação para identificar os métodos de teste.

Fornece asserções para testar os resultados esperados.

Fornece corredores de teste para executar testes.

Os testes JUnit permitem escrever código mais rapidamente, aumentando a qualidade

JUnit é elegantemente simples. É menos complexo e leva menos tempo.

Os testes JUnit podem ser executados automaticamente e verificam seus próprios resultados e fornecem feedback imediato. Não há necessidade de vasculhar manualmente um relatório dos resultados dos testes.

Os testes JUnit podem ser organizados em conjuntos de testes contendo casos de teste e até outros conjuntos de testes.

O Junit mostra o progresso do teste em uma barra verde se o teste estiver indo bem e fica vermelha quando um teste falha.

AKKI
fonte
2

Eu tenho uma perspectiva um pouco diferente de por que o JUnit é necessário.

Na verdade, você pode escrever todos os casos de teste, mas é complicado. Aqui estão os problemas:

  1. Em vez de System.out, podemos adicionar if(value1.equals(value2))e retornar 0 ou -1 ou mensagem de erro. Nesse caso, precisamos de uma classe de teste "principal" que execute todos esses métodos e verifique os resultados e mantenha quais casos de teste falharam e quais foram aprovados.

  2. Se você quiser adicionar mais alguns testes, precisará adicioná-los a essa classe de teste "principal" também. Alterações no código existente. Se você deseja detectar automaticamente casos de teste de classes de teste, precisará usar a reflexão.

  3. Todos os seus testes e sua classe principal para executar testes não são detectados pelo eclipse e você precisa escrever configurações personalizadas de depuração / execução para executar esses testes. Você ainda não vê essas saídas coloridas de verde / vermelho.

Aqui está o que o JUnit está fazendo:

  1. Possui assertXXX()métodos úteis para imprimir mensagens de erro úteis das condições e comunicar resultados à classe "principal".

  2. A classe "main" é chamada runner, que é fornecida pelo JUnit, portanto não precisamos escrever nenhuma. E detecta os métodos de teste automaticamente por reflexão. Se você adicionar novos testes com @Testanotação, eles serão detectados automaticamente.

  3. O JUnit também possui integração com eclipse e maven / gradle, por isso é fácil executar testes e você não precisará escrever configurações de execução personalizadas.

Eu não sou especialista em JUnit, então é isso que entendi a partir de agora e que acrescentará mais no futuro.

Sreekar
fonte
Acho que na primeira parte você escreveu o que faríamos se o JUnit não estivesse lá para tornar o teste de unidade um pouco melhor do que as declarações system.out.println. Pode ser que o JUnit é o resultado de tais tentativas de alguns programadores e eles sentiram a necessidade de escrever uma estrutura de teste separada para executar essa automação, assim nasceu o JUnit.
Saurabh Patil
1

Você não pode escrever nenhum caso de teste sem usar a estrutura de teste; caso contrário, terá que escrever seu quadro de teste para dar justiça aos seus casos de teste. Aqui estão algumas informações sobre o JUnit Framework, além de que você pode usar a estrutura TestNG.

O que é o Junit?

O Junit é uma estrutura de teste amplamente usada junto com a linguagem de programação Java. Você pode usar essa estrutura de automação para testes de unidade e de interface do usuário. Isso nos ajuda a definir o fluxo de execução do nosso código com diferentes anotações. O Junit baseia-se na idéia de "primeiro teste e depois codificação", o que nos ajuda a aumentar a produtividade dos casos de teste e a estabilidade do código.

Recursos importantes do Junit Testing -

  1. É uma estrutura de teste de código aberto que permite aos usuários escrever e executar casos de teste de forma eficaz.
  2. Fornece vários tipos de anotações para identificar métodos de teste.
  3. Fornece diferentes tipos de asserções para verificar os resultados da execução do caso de teste.
  4. Ele também fornece os executores de teste para executar testes de maneira eficaz.
  5. É muito simples e, portanto, economiza tempo.
  6. Ele fornece maneiras de organizar seus casos de teste na forma de fatos de teste.
  7. Fornece resultados de casos de teste de maneira simples e elegante.
  8. Você pode integrar o jUnit com Eclipse, Android Studio, Maven & Ant, Gradle e Jenkins
anuja jain
fonte
0

JUNIT é o método geralmente aceito pelo desenvolvedor java. Onde eles podem fornecer entrada esperada semelhante à função e decidir de acordo que o código escrito está perfeitamente escrito ou se o caso de teste falhar, uma abordagem diferente também pode precisar ser implementada. O JUNIT agilizará o desenvolvimento e garantirá os 0 defeitos na função.

mohit sarsar
fonte
0

JUNTA: OBSERVE E AJUSTE

Aqui está a minha perspectiva da JUNIT.

O JUNIT pode ser usado para:
1) Observar o comportamento do sistema quando uma nova unidade é adicionada nesse sistema.
2) Faça ajustes no sistema para receber a "nova" unidade no sistema.
O que? Exatamente.

Vida real, por exemplo.

Quando seu parente visitar o quarto de sua faculdade,
1) Você fingirá ser mais responsável.
2) Você manterá todas as coisas onde elas devem estar, como sapatos na sapateira não na cadeira, roupas no armário e não na cadeira.
3) Você vai se livrar de todo o contrabando.
4) você começará a limpeza em todos os dispositivos que possui.

Em termos de programação

Sistema: Seu código
UNIDADE: nova funcionalidade.
Como a estrutura JUNIT é usada para a linguagem JAVA, JUNIT = UNIDADE JAVA (pode ser).

Suponha que você já tenha um código à prova de balas, mas veio um novo requisito e você deve adicionar o novo requisito ao seu código. Esse novo requisito pode quebrar seu código para alguma entrada (testcase).

A maneira mais fácil de adaptar essa alteração é usando o teste de unidade (JUNIT).
Para isso, você deve escrever vários casos de teste para seu código ao criar sua base de código. E sempre que surgir um novo requisito, basta executar todos os casos de teste para ver se algum deles falha. Se Não, você é um artista BadA ** e está pronto para implantar o novo código.
Se alguma das caixas de teste falhar, você altera seu código e executa novamente as caixas de teste até obter o status verde.

Rohit Singh
fonte