Skip to main content

Como escrever testes unitários em Java

Escrito por

Lewis Gavin

feature java dto

18 de novembro de 2022

0 minutos de leitura

Testar é uma prática recomendada essencial no desenvolvimento de software. Os testes unitários são uma das várias estratégias que podemos usar para garantir que nosso código seja funcional e eficiente. Como pessoas desenvolvedoras, podemos escrever testes unitários para verificar componentes individuais (unidades) do código da aplicação, como um método específico. A ideia é escrever um ou mais testes unitários para cada parte do código e executá-los sempre que houver uma alteração, para detectar defeitos assim que forem introduzidos na base de código. Para código novo, os testes devem ser adicionados enquanto o código é escrito (ou até mesmo antes). Para código existente, se ainda não houver testes, eles devem ser adicionados para cobrir muitos — se não todos — os caminhos de execução da lógica da aplicação.

Os testes unitários têm como objetivo eliminar todas as dependências externas e confirmar que um trecho específico do código se comporta como esperado em diferentes situações. Isso pode envolver chamar um método repetidamente, cada vez com novos parâmetros, e verificar se ele retorna o valor correto, lança uma exceção ou executa uma tarefa específica.

Entre os frameworks de testes unitários disponíveis, muitos desenvolvedores Java usam o JUnit. Neste post, vamos aprender a instalar e usar o JUnit 5 para escrever testes unitários para um código Java. Vamos usar o ambiente de desenvolvimento integrado (IDE) VSCode para escrever os testes e o Java 11 com Maven para executá-los. No entanto, ferramentas como Eclipse e IntelliJ funcionam de maneira semelhante e também permitem acompanhar o tutorial.

Para conhecer mais ferramentas de análise de código Java, confira nossas ferramentas de revisão de código Java ou nosso artigo sobre segurança em Java para aprender os conceitos básicos.

Como escrever seu primeiro teste unitário em Java

Nesta seção, vamos instalar o framework JUnit e escrever alguns testes unitários. Mais adiante, vamos executar os testes neste projeto de código de exemplo. O projeto já vem configurado com o código da aplicação e um modelo de classe de teste com tudo o que precisamos para começar a adicionar testes.

Primeiro, antes de começar a escrever os testes, vamos analisar o código de exemplo.

package com.snyk.unittestdemo;

public class App 
{
    public static void main( String[] args )
    {
        try {
            System.out.println(generateUpdateUserFirstNameStatement("John", "abcdef1234567"));
        }
        catch (Error e){
            e.printStackTrace();
        }
    }

    public static String generateUpdateUserFirstNameStatement(String firstName, String userId) throws Error{
        if(userId.contains(";") || firstName.contains(";")){
            throw new Error("parameters may contain SQL injection");
        }
        return "UPDATE TABLE user SET first_name = '" + firstName + "' WHERE user_id = '" + userId +"';" ;
    }

}

Nossa aplicação tem um método main que chama o método generateUpdateUserFirstNameStatement e exibe o resultado. Se uma chamada ao método generateUpdateUserFirstNameStatement gerar um erro, a aplicação captura a exceção e exibe o rastreamento da pilha.

O método generateUpdateUserFirstNameStatement é simples: recebe dois parâmetros do tipo string e os usa para gerar uma instrução SQL UPDATE. O código gera um erro se algum dos parâmetros contiver ponto e vírgula, pois isso provavelmente indica uma tentativa de injeção de SQL.

Esse método é um excelente exemplo de caso de uso para testes unitários, pois queremos confirmar seu funcionamento em dois cenários. Primeiro, quando passamos duas strings comuns como parâmetros, esperamos receber uma string que contenha a instrução SQL. Depois, quando fornecemos como segundo parâmetro uma string que contém uma injeção de SQL, esperamos que o método gere um erro. Podemos testar ambos os casos com testes unitários.

Para começar, precisamos instalar alguns pacotes Maven, incluindo o maven-surefire-plugin, que inclui a biblioteca JUnit. Também precisamos informar ao Maven que nossa aplicação depende do JUnit durante a execução dos testes.

Vamos abrir o arquivo pom.xml fornecido e adicionar o código de dependência a seguir na seção correspondente, na linha 21.

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.11</version>
  <scope>test</scope>
</dependency>

Quando terminar, salve o arquivo.

Agora podemos usar o Maven para instalar os pacotes. Para fazer isso no VSCode, expanda o menu Maven no canto inferior esquerdo, clique com o botão direito do mouse no projeto e selecione install. Se você estiver usando outra IDE, o processo pode ser diferente.

Visual Studio Code exibindo o pom.xml de um projeto Java com uma dependência de teste do JUnit 4.11 e o menu de comandos do Maven aberto

Se não souber como fazer isso na sua IDE, você também pode instalar as dependências pela linha de comando. No terminal, acesse o diretório raiz do projeto e execute o seguinte comando Maven para instalar as dependências.

Observação: se o Maven não estiver instalado na sua máquina, siga as instruções de instalação antes de executar este comando.

mvn install -f pom.xml

Ao concluir, o Maven terá instalado os plugins especificados no nosso pom.xml file, incluindo a biblioteca JUnit. Ao executar os testes, o Maven sabe que deve usar o JUnit, definido como dependência de teste por meio de <scope>test</scope> na configuração.

Saída do terminal mostrando testes unitários em Java sem falhas e o resultado verde “BUILD SUCCESS” do Maven.

Como podemos ver na saída acima, ao final da instalação são exibidas as informações relacionadas aos testes. No entanto, ainda não configuramos nenhum teste, então o número de testes executados foi 0. Vamos mudar isso adicionando dois testes unitários à aplicação.

Para começar, abra o arquivo src/test/java/com/snyk/unittestdemo/AppTest.java, que o Maven executará para rodar nossos testes. Primeiro, precisamos importar os pacotes JUnit necessários. Adicione as duas importações a seguir no início do arquivo:

import org.junit.Test;

import static org.junit.Assert.assertTrue;

A primeira importação nos dá acesso à anotação de teste do JUnit, que permite definir nossos métodos como testes. A segunda importação é do método assertTrue, fornecido pelo JUnit. Usamos esse método para verificar se uma condição resulta em verdadeiro. Se sim, o teste será aprovado. Caso contrário, será reprovado.

Vamos adicionar nosso primeiro teste, que verificará se o método generateUpdateUserFirstNameStatement retorna o resultado correto quando recebe duas strings comuns. Dentro da classe AppTest, adicione o método a seguir.

@Test
public void shouldGenerateUpdateStatement()
{
    String actual = App.generateUpdateUserFirstNameStatement("John", "abcdef1234567");
    String expected = "UPDATE TABLE user SET first_name = 'John' WHERE user_id = 'abcdef1234567';";
    assertTrue( actual.equals(expected) );
}

Definimos o método como um teste usando a anotação @Test. Em seguida, usamos o método importado assertTrue para verificar a saída do método generateUpdateUserFirstNameStatement. Passamos duas strings — John e abcdef1234567 — e queremos verificar se a função retorna a string esperada. Se o método estiver funcionando corretamente, o teste será aprovado.

Salve o arquivo e execute o teste para verificar se ele é aprovado. No VSCode, clique com o botão direito do mouse no projeto Maven e selecione test.

Visual Studio Code exibindo um AppTest do JUnit em Java e a saída do terminal do Maven, com um teste aprovado e uma compilação bem-sucedida

Você também pode executar este comando pelo terminal. Para isso, abra o terminal, acesse a pasta do projeto e execute o seguinte comando.

mvn test -f pom.xml

Esse comando instrui o Maven a compilar a aplicação e executar os testes. A saída dos testes agora deve indicar que um teste foi executado e que não houve falhas.

Saída do terminal mostrando a execução de um teste unitário em Java, sem falhas, erros ou testes ignorados, seguida da mensagem BUILD SUCCESS.

Agora, vamos modificar um pouco o método generateUpdateUserFirstNameStatement, alterando a instrução UPDATE que a função retorna. Execute os testes novamente para ver como o Maven exibe uma falha.

Saída do terminal mostrando um teste unitário de Java com falha, incluindo uma falha de asserção em AppTest.shouldGenerateUpdateStatement e uma falha na compilação do Maven.

A saída agora muda e informa que um teste falhou. Abaixo do cabeçalho Results:, você pode ver o número e os tipos de falha. Neste caso, os resultados identificam o método de teste generateUpdateUserFirstNameStatement e o número da linha em que ocorreu a falha (linha 14). Lembre-se de desfazer as alterações que fizeram a função parar de funcionar.

Também podemos testar se nossa função gera um erro como esperado, usando uma abordagem um pouco diferente do método assertTrue. Para começar, adicione o seguinte método de teste à classe de teste.

@Test(expected = Error.class)
public void shouldThrowErrorForSQLInjection()
{
    App.generateUpdateUserFirstNameStatement("John", "abcdef1234567; DROP TABLE user;");
}

Aqui, usamos a anotação @Test para indicar um valor esperado. Esperamos que nosso método gere uma instância da classe Error, pois foi assim que programamos o tratamento de uma tentativa de injeção de SQL em qualquer um dos parâmetros. Agora podemos executar os testes e verificar se eles são aprovados.

Saída do terminal mostrando a execução bem-sucedida de testes unitários em Java: dois testes, nenhuma falha ou erro e a mensagem BUILD SUCCESS.

Implementamos com sucesso nossos primeiros testes unitários em Java com JUnit. Com eles, podemos garantir que nosso código funcione como esperado e produza os resultados corretos. Se isso não acontecer, o teste será reprovado.

É essencial proteger nossas aplicações contra vulnerabilidades, como as que nos expõem a injeções maliciosas de SQL. Adicionar um teste como este pode evitar que uma pessoa desenvolvedora introduza essa vulnerabilidade no futuro. Os testes aumentam a confiança no código e oferecem uma rede de segurança para quem for desenvolvê-lo depois. Saiba mais sobre as práticas recomendadas de segurança em Java neste artigo.

Snyk Code é outra ferramenta que pode detectar práticas inseguras que talvez passem despercebidas pelas pessoas desenvolvedoras e não sejam cobertas por testes escritos. Isso inclui identificar código vulnerável a injeções de SQL e informações confidenciais inseridas diretamente no código, como segredos de API e dados de identificação pessoal.

O teste define um contrato sobre o que o método deve fazer. Se alguém quiser atualizar o método generateUpdateUserFirstNameStatement e, sem querer, comprometer seu funcionamento — como fizemos no segundo teste —, o teste unitário falhará e dará informações sobre o problema. Essa prática acrescenta uma camada de proteção contra a quebra de funcionalidades essenciais e a liberação de alterações instáveis ou perigosas em uma aplicação de produção.

Conclusão

Como aprendemos neste tutorial, escrever testes unitários é uma maneira simples de garantir que nosso código se comporte como esperado em diferentes cenários. Além disso, os testes unitários oferecem uma camada de proteção contra falhas em projetos colaborativos, evitando que outras pessoas desenvolvedoras quebrem nosso código acidentalmente ou introduzam vulnerabilidades. Embora não sejam a única forma de testar aplicações, os testes unitários devem fazer parte do conjunto de ferramentas de toda pessoa desenvolvedora.

Confira estes artigos relacionados para saber mais sobre Java:

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.

Leia mais

Blog

Modelos de ponta encontraram as vulnerabilidades. Só o atacante encontrou as cadeias.

A análise estática encontrou as falhas, mas só os testes de ataque em aplicações ativas provaram como elas poderiam ser encadeadas para causar invasões. Uma comparação entre Evo COS, Claude Security e Claude Code Security.

feature insights context
Blog

Os ataques autônomos já chegaram. A defesa precisa acompanhar o ritmo.

Os atacantes autônomos estão reduzindo o tempo disponível para a defesa. Saiba como a descoberta, a correção, a validação e a prevenção contínuas ajudam as equipes de segurança a acompanhar esse ritmo.

Blog

Por que agentes de programação com IA continuam criando falhas de controle de acesso

Agentes de programação com IA podem gerar uma lógica de autorização que compila e passa pela revisão, mas permite que um tenant acesse os dados de outro. Saiba por que é difícil detectar falhas de controle de acesso e como evitá-las.