O que é teste de unidade em Java?
Antes de começarmos a aprender JUnit em Java, vamos dar uma breve visão geral do que é o teste de unidade e por que ele é tão popular (se você já sabe disso, pule para 'Como faço para escrever um teste JUnit em Java?'). O teste de unidade em Java torna o desenvolvimento de software em larga escala muito mais eficiente e sem esforço. Ele pode ajudar tanto os indivíduos quanto as equipes a reduzir incontáveis horas de depuração e simplificar imensamente o processo de colaboração.
https://junit.org/junit4/
A ideia essencial do teste de unidade é esta: escrever testes atômicos de recursos individuais (chamados de testes de unidade) e adicionar lentamente mais recursos após testar e garantir que os anteriores funcionem. É uma ideia extremamente simples, mas poderosa. Como exemplo de como pode ser esse processo, imagine que você esteja construindo uma calculadora científica virtual. Além dos aparentes operadores aritméticos (
+
,
-
,
x
,
%
), esta calculadora teria recursos avançados que requerem outros sub-recursos para funcionar dentro dela. Para calcular expoentes, sua calculadora precisa ser capaz de multiplicar corretamente. Portanto, uma abordagem de teste de unidade para construir e testar esta calculadora seria:
- Escreva uma função de adição. Teste com cuidado, troque, repita até funcionar.
- Faça o mesmo para as funções de subtração, multiplicação e divisão.
- Use esses operadores básicos para escrever funções de operador mais avançadas, como expoentes, e teste essas funções também.
Isso garante que os recursos que constroem outros sub-recursos menores não apenas funcionem corretamente por conta própria, mas também não tenham sub-recursos defeituosos dentro deles. Por exemplo, se estou testando a função expoente e algo está errado, sei que o bug provavelmente não está no subrecurso de multiplicação, porque a função de multiplicação já foi amplamente testada. Isso elimina enormemente a quantidade total de código que preciso rastrear e inspecionar para encontrar o bug. Esperançosamente, este exemplo trivial deixa claro como o processo de pensamento em torno do Teste de Unidade é estruturado. Mas como o teste de unidade interage com o restante do processo de desenvolvimento de software? E se você tiver recursos ainda mais complexos, que precisam funcionar e se comunicar juntos? O teste de unidade é insuficiente para garantir que esses recursos complexos possam funcionar adequadamente juntos. Na verdade, é apenas o primeiro passo dos Quatro Níveis de Teste de Software (eu uso letras maiúsculas porque me refiro ao padrão da indústria ou à abordagem mais comum para testar software). As três últimas etapas são
Teste de Integração ,
Teste de Sistema e
Teste de Aceitação. Todos eles provavelmente significam exatamente o que você pensa que significam, mas deixe-me esclarecer: o teste de integração é o que faríamos para garantir que os mencionados acima, “recursos complexos”, interajam adequadamente uns com os outros. (por exemplo, certificar-se de que a calculadora pode lidar com “3 + 7 * 4 - 2”) O teste do sistema está testando o design geral de um sistema específico; geralmente existem vários sistemas de recursos complexos trabalhando juntos em um produto, então você os agrupa em sistemas e os testa individualmente. (por exemplo, se você estivesse construindo uma calculadora gráfica, você primeiro construiria o 'sistema' aritmético para lidar com números, testando até que funcionasse como pretendido, e então você construiria e testaria o 'sistema' gráfico para lidar com a retirada, como seria construída a partir do sistema aritmético). O teste de aceitação é um teste no nível do usuário; é ver se todos os sistemas podem trabalhar em sincronia para criar um produto acabado pronto para ser aceito pelos usuários (por exemplo, usuários testando a calculadora). Às vezes, os desenvolvedores de software podem ignorar essa etapa final do processo, pois as empresas geralmente fazem com que outros funcionários implantem testes de usuário (beta) separadamente.
Como faço para escrever um teste JUnit em Java?
Agora que você tem uma ideia mais clara dos benefícios e limitações dos testes de unidade, vamos dar uma olhada em algum código! Estaremos usando uma estrutura de teste Java popular chamada JUnit (outra popular é TestNG, que você também pode usar se quiser. Eles são muito semelhantes, sintaticamente; TestNG é inspirado em JUnit). Você pode baixar e instalar o JUnit
aqui . Para este código de exemplo, continuaremos com o exemplo de 'calculadora científica' que mencionei anteriormente; é bem simples de entender, e o código de teste é super fácil. A prática convencional é escrever classes de teste separadas para cada uma de suas classes, então é isso que faremos. Vamos supor que, neste ponto, temos um
Math.java
arquivo com todas as funções matemáticas nele (incluindo
Math.add
) e estamos escrevendo um
MathTests.java
arquivo no mesmo pacote. Agora vamos configurar as instruções de importação e o corpo da classe: (POSSÍVEL JUnit ENTREVISTA PERGUNTA: Você pode ser perguntado onde colocar seu teste JUnit e se precisa ou não importar seus arquivos de origem. Se você estiver escrevendo suas classes de teste no mesmo pacote que suas classes principais, então você não precisa de nenhuma declaração de importação para seus arquivos de origem na classe de teste. Caso contrário, certifique-se de importar seus arquivos de origem!)
import org.junit.jupiter.Test; //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :)
public class MathTests {
//...
}
A primeira declaração de importação nos dá o
@Test
cabeçalho. Escrevemos '
@Test
' diretamente no topo de cada definição de função de teste, para que o JUnit saiba que este é um teste de unidade singular que pode ser executado separadamente. Mais tarde, mostrarei como você pode executar testes de unidade específicos usando este cabeçalho. A segunda declaração de importação nos poupa um pouco de digitação. A principal função JUnit que usamos para testar nossas funções é to
Assert.assertEquals()
, que recebe dois parâmetros (valor real e valor esperado) e garante que sejam iguais. Ter esta segunda declaração de importação nos permite apenas digitar '
assertEquals(...
' em vez de ter que especificar toda vez de qual pacote ele faz parte. Agora vamos escrever um caso de teste muito simples para verificar se 2 + 2 é realmente 4!
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)
public class MathTests {
@Test
public void add_twoPlusTwo_returnsFour(){
final int expected = 4;
final int actual = Math.add(2, 2);
assertEquals(“2+2 is 4”, actual, expected);
}
}
Vamos analisar cada uma das cinco linhas da função de teste e o que elas fazem: Linha 5: Este
@Test
cabeçalho especifica que a definição da função abaixo
add_twoPlusTwo_returnsFour()
é de fato uma função de teste que o JUnit pode executar separadamente. Linha 6: Esta é a assinatura da função para nosso caso de teste. Os casos de teste são sempre muito singulares; eles testam apenas um exemplo específico, como 2+2=4. É uma convenção nomear seus casos de teste no formato “
[function]_[params]_returns[expected]()
”, onde
[function]
é o nome da função que você está testando,
[params]
são os parâmetros de exemplo específicos que você está testando e
[expected]
é o valor de retorno esperado da função. As funções de teste quase sempre têm um tipo de retorno '
void
' porque o ponto principal de toda a função é executar
assertEquals
, que enviará para o console se seu teste passou ou não; você não precisa que nenhum outro dado seja retornado em nenhum lugar. Linha 7: Declaramos uma
final
variável ' ' do tipo de retorno de
Math.add (int)
, e a chamamos de 'esperada' por convenção. Seu valor é a resposta que esperamos (4). Linha 8: Declaramos uma
final
variável ' ' do tipo de retorno de
Math.add (int)
, e a chamamos de 'real' por convenção. Seu valor é o resultado de
Math.add(2, 2)
. Linha 9: A linha dourada. Esta é a linha que compara o real e o esperado e nos diz que passamos no teste somente se forem iguais. O primeiro parâmetro passado “2+2 é 4” é uma descrição da função de teste.
E se minha função lançar uma exceção?
Se o seu exemplo de teste específico deve lançar uma exceção em vez de afirmar que um valor real e esperado são iguais, o JUnit tem uma maneira de esclarecer isso no
@Test
cabeçalho. Vamos dar uma olhada em um exemplo abaixo. Assumindo que temos uma função
Math.java
chamada
Math.divide
, queremos ter certeza de que as entradas não podem ser divididas por 0. Em vez disso, tentar chamar
Math.divide(a, 0)
qualquer valor 'a' deve lançar uma exceção (
ArithmeticException.class
). Nós especificamos isso no cabeçalho como tal:
import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :)
public class MathTests {
@Test (expectedExceptions = ArithmeticException.class)
public void divide_byZero_throwsException() throws ArithmeticException{
Math.divide(1, 0);
}
}
Você pode ter mais de uma exceção para
expectedExceptions
, apenas certifique-se de usar colchetes e vírgulas para listar suas classes de exceção, como:
expectedException = {FirstException.class, SecondException.class, … }
Como executo meus testes JUnit em Java?
Como adicionar JUnit ao IntelliJ:
https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Você pode executar seu projeto como normalmente executaria os testes. A execução de todos os testes em uma classe de teste os executará em ordem alfabética. No JUnit 5, você pode adicionar uma prioridade aos testes adicionando uma
@Order
tag. Um exemplo:
@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }
@Test
@Order (1)
public void b_test() { … }
…
}
Mesmo que
a_test()
venha antes em
b_test()
ordem alfabética e no código,
b_test()
vai rodar antes
a_test()
aqui, porque 1 vem antes de 2 em Order. Isso é tudo para o básico do JUnit. Agora, vamos abordar algumas perguntas comuns sobre entrevistas JUnit e aprender um pouco mais sobre JUnit ao longo do caminho!
Perguntas da entrevista JUnit (informações adicionais)
Aqui eu coletei as perguntas mais populares da entrevista JUnit. Se você tiver algo a acrescentar - sinta-se à vontade para fazê-lo nos comentários abaixo.
P: Qual método você pode chamar em seu método de teste para falhar automaticamente em um teste? A: fail(“descrição do erro aqui!”); P: Você está testando uma classe Dog; para testar um objeto Dog, você precisa instanciá-lo antes de executar testes nele. Então você escreve uma função setUp() para instanciar o Dog. Você só deseja executar esta função uma vez durante todo o teste. O que você deve colocar diretamente acima da assinatura da função setUp() para que o JUnit saiba que deve executar setUp() antes de executar os testes? R: @BeforeClass (@BeforeAll no JUnit 5) P:Qual deve ser a assinatura da função setUp() descrita acima? A: vazio estático público. Qualquer função com @BeforeClass (@BeforeAll no JUnit 5) ou @AfterClass (@AfterAll no JUnit 5) precisa ser estática. P: Você terminou de testar a classe Dog. Você escreve a função void tearDown() que limpa os dados e imprime informações no console após cada teste. Você deseja que esta função seja executada após cada teste. O que você deve colocar diretamente acima da assinatura da função tearDown() para que JUnit saiba executar tearDown() depois de executar cada teste? R: @After (@AfterEach no JUnit 5)
GO TO FULL VERSION