Curso JUnit: #4 - Melhorando nossos testes

Pode parecer pouca coisa, mas o que você viu até agora já permite que você escreva testes para a maioria dos cenários que você vai se deparar, mas, é claro, existem vários pontos de melhoria. Nesse post vamos ver como melhorar ainda mais o nosso código para cobrir todas as nossas regras de negócio.

Vamos voltar ao método testGerarSenhaNumericaComOitoDigitos. Poderíamos melhorar esse método realizando duas implementações. A primeira é fazer ele receber por parâmetro uma lista de valores, conforme fizemos com o outro método.

@ParameterizedTest
@ValueSource(ints = { 4, 5, 8, 12, 99, 100 })
@DisplayName("Deve ser possível gerar uma senha numérica com quantidade válida de digitos")
public void testGerarSenhaNumerica(int tamanho) throws Exception {
    // Arrange
    GeradorDeSenhas geradorDeSenhas = new GeradorDeSenhas();

    // Act
    String senhaGerada = geradorDeSenhas.gerarSenhaNumerica(tamanho);

    // Assert
    Assertions.assertEquals(tamanho, senhaGerada.length());
    Assertions.assertTrue(senhaGerada.matches("\\d+"));
}

Aproveitei e também mudei o nome e o DisplayName do método para fazer mais sentido, visto que agora ele não testa apenas se tem 8 dígitos.

Outra implementação que eu fiz foi realizar uma asserção para garantir que a senha gerada tem apenas números.

Na maioria dos casos é uma boa prática separar as asserções em métodos independentes, mas nesse caso eu não vejo problema manter as duas asserções no mesmo método visto que estão validando coisas do mesmo cenário.

Note que na segundo asserção eu utilizei uma Regex (Expressão Regular). Se você quiser entender mais sobre o assunto, confira meu Vídeo sobre Expressões Regulares

Vamos aproveitar que já estamos com a mão na massa e vamos implementar a possibilidade de gerar uma senha alpha numérica e também o seu teste.

Implementação para Gerar senha alpha numérica

public String gerarSenhaAlphaNumerica(int tamanho) throws Exception {

    if (tamanho < 4 || tamanho > 100) {
        throw new Exception("Só é possível gerar senhas com comprimento entre 4 e 100 digitos");
    }

    String senha = "";
    Random random = new Random();

    int MAIOR_VALOR_INTEIRO_POSSIVEL = 9;
    int MAIOR_VALOR_CHAR_POSSIVEL = 25;

    for (int i = 0; i < tamanho; i++) {
        if (i % 2 == 0) {
            int numeroAleatorio = random.nextInt(MAIOR_VALOR_INTEIRO_POSSIVEL + 1);
            senha = senha.concat(String.valueOf(numeroAleatorio));
        } else {
            char letraAleatoria = (char) ('a' + random.nextInt(MAIOR_VALOR_CHAR_POSSIVEL + 1));
            senha = senha.concat(String.valueOf(letraAleatoria));
        }
    }

    return senha;

}

Teste

@ParameterizedTest
@ValueSource(ints = { 4, 6, 8, 40, 66, 100 })
@DisplayName("Deve ser possível gerar uma senha alpha numérica")
public void testGerarSenhaAlphaNumerica(int tamanho) throws Exception {

    // Arrange
    GeradorDeSenhas geradorDeSenhas = new GeradorDeSenhas();
    String regex = "^(?=.*[a-zA-Z])(?=.*\\d).+$";
    Pattern pattern = Pattern.compile(regex);

    // Act
    String senhaGerada = geradorDeSenhas.gerarSenhaAlphaNumerica(tamanho);

    // Assert
    Matcher matcher = pattern.matcher(senhaGerada);
    Assertions.assertEquals(tamanho, senhaGerada.length());
    Assertions.assertTrue(matcher.matches());

}

Acredito que a única coisa que possa ter criado alguma confusão no entendimento dos códigos acima é o trecho

String regex = "^(?=.*[a-zA-Z])(?=.*\\d).+$";

Mas como eu disse, você pode assistir meu Vídeo sobre Expressões Regulares e esse código começará a fazer mais sentido para você, porém, por hora apenas entenda que ele analisa se existe pelo menos uma ocorrência de uma letra e de um número inteiro.

E para finalizar temos que garantir que seja possível criar uma senha forte, para isso vamos reaproveitar o método de criar senhas alpha numéricas.

public String gerarSenhaAlphaNumericaForte(int tamanho) throws Exception {

    String senha = gerarSenhaAlphaNumerica(tamanho);
    Random random = new Random();
    char[] digitosEspeciais = { '!', '@', '$', '&', '-', '_', '.' };

    int posicaoAleatoria = random.nextInt(senha.length());
    char digitoEspecial =  digitosEspeciais[random.nextInt(digitosEspeciais.length)];

    char[] chars = senha.toCharArray();
    chars[posicaoAleatoria] = digitoEspecial;
    senha = new String(chars);

    return senha;

}

E o teste ficaria assim

@ParameterizedTest
@ValueSource(ints = { 4, 6, 8, 40, 66, 100 })
@DisplayName("Deve ser possível gerar uma senha alpha numérica forte")
public void testGerarSenhaAlphaNumericaForte(int tamanho) throws Exception {

    // Arrange
    GeradorDeSenhas geradorDeSenhas = new GeradorDeSenhas();
    String regex = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[!@\\$&\\-_.]).+$";
    Pattern pattern = Pattern.compile(regex);

    // Act
    String senhaGerada = geradorDeSenhas.gerarSenhaAlphaNumericaForte(tamanho);

    // Assert
    Matcher matcher = pattern.matcher(senhaGerada);
    Assertions.assertEquals(tamanho, senhaGerada.length());
    Assertions.assertTrue(matcher.matches());

}

Perceba que o teste é muito parecido com o testGerarSenhaAlphaNumerica, com excessão da Expressão Regular.

Existem mais alguns cenários que poderiam (e deveriam ser testados) como por exemplo verificar lançamento de Exception ao gerar senha alpha numérica e ao gerar senha forte, mas a ideia desse treinamento não é cobrir todas as possibilidades, mas sim apresentar como funciona a escrita de testes no JUnit.

Refatoração sem medo

Todo o código de exemplo aqui exposto foi feito de uma maneira extremamente simplória. Você agora poderia sem medo refatorar esse código melhorando-o sem medo de quebrar, visto que você agora tem uma automação garantindo o correto funcionamento dele.

Bem, esse é o conhecimento básico que você precisa ter para começar a trabalhar com testes automatizados em seus projetos. Em breve trarei uma parte 2 desse treinamento onde veremos exemplos mais complexos no Spring Boot. Até breve, amigos!!!