Muito se fala sobre testes e TDD e sobre os benefícios que eles proporcionam no desenvolvimento de software, mas ainda há muitas dúvidas que surgem no dia-a-dia e que, às vezes, desmotivam ou levam a escassez cada vez maior desta prática tão importante.
O que vou abordar neste artigo é algo bem específico dentro do mundo dos testes e que muitos desenvolvedores ainda têm dúvida sobre como proceder nesta situação. Como testar um método void?
Sabemos que o conceito de testar um método é basicamente:
“Testar um método consiste em validar ou invalidar os dados de entrada e de saída do mesmo”
Legal, mas de cara já vimos que um método void não atende a esta premissa, então, como é possível testar o valor de saída de um método que não tem saída?
Esta pergunta tem duas respostas e vou falar sobre elas nos exemplos a seguir.
Há duas formas de se testar métodos void:
Side Effects (Efeito Colateral)
Os métodos, em alguns casos, alteram o estado de alguma coisa, seja de um banco de dados, de um arquivo, da memória, ou de qualquer outra coisa. O simples fato de alterar um estado nos dá a oportunidade de testar o efeito colateral que este método provocou.
Se temos a alteração de um estado então temos como verificar seu estado atual e comparar com o estado após o método ser executado. Vou dar um exemplo mais prático.
Temos um método que cria um arquivo .txt na raiz do C:. Este método recebe um nome que será o nome do arquivo. Exemplo básico da estrutura:
public class Arquivo
{
public void CriarArquivo(string nome)
{
var path = $@"C:\{nome}.txt";
using (var tw = new StreamWriter(path, true))
{
tw.WriteLine("Arquivo criado");
}
}
public void EnviaEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("E-mail obrigatório");
}
if (!ValidaEmail(email))
{
throw new ArgumentException("E-mail inválido");
}
try
{
var client = new SmtpClient("servidorSMTP")
{
UseDefaultCredentials = false,
Credentials = new NetworkCredential("usuario", "senha")
};
var mailMessage = new MailMessage
{
From = new MailAddress("noreply@dominio.com")
};
mailMessage.To.Add(email);
mailMessage.Body = "Corpo do email";
mailMessage.Subject = "Assunto";
client.Send(mailMessage);
}
catch (Exception ex)
{
throw new ArgumentException(ex.Message);
}
}
private bool ValidaEmail(string email)
{
try
{
var addr = new MailAddress(email);
return addr.Address == email;
}
catch
{
return false;
}
}
}
Neste método especificamente, não temos um retorno, pode ser que o seu domínio necessite de uma confirmação, neste caso poderíamos retornar um booleano, mas aqui vamos usar VOID para exemplificar.
Neste exemplo fica claro que há uma alteração de estado. Antes de executarmos o método não existia um arquivo, mas depois que executamos o método o arquivo foi criado, ou seja, alteramos o estado da raiz do diretório C:.
Sabendo disto, podemos testar e validar um método void verificando se o arquivo existe.
Veja abaixo como ficaria um teste básico do nosso método:
[Fact]
public void Testa_Criacao_Arquivo()
{
var arquivo = new Arquivo();
var nomeArquivo = "NomeArquivo";
arquivo.CriarArquivo(nomeArquivo);
Assert.True(File.Exists($@"C:\{nomeArquivo}.txt"));
}
Mas e quando temos um método void que não altera estado? E se temos um método de envio de e-mail, envio de SMS ou impressão de arquivo que retorna void? Não há alteração de estado, não há o que comparar para validarmos se o o método foi executado com sucesso.
É aí que entra a segunda forma de se testar um método void.
Exceptions (Excessões)
Mesmo que um método void não altere estado ainda assim é possível validar seu comportamento e sua funcionalidade.
Podemos utilizar as Exceptions do .Net para verificar se houve algum erro no método. Caso o método retorne uma Exception assumimos que o método falhou, caso não retorne nenhuma Exception assumimos que o método executou sua funcionalidade com sucesso.
Para que isto seja possível, é necessário fazermos uso da Exception para cada step do método. Quanto mais Exceptions mais garantimos a integridade do método testado.
Uma vez nosso método bem escrito e adequadamente coberto por Exceptions, basta escrevermos o teste que, se receber uma Exception, falha.
No código abaixo podemos ver vários testes que verificam cada etapa do método de envio de e-mail:
[Fact]
public void Testa_Envio_Email_Vazio()
{
var arquivo = new Arquivo();
var ex = Assert.Throws(() => arquivo.EnviaEmail(""));
Assert.Equal("E-mail obrigatório", ex.Message);
}
[Fact]
public void Testa_Envio_Email_Invalido()
{
var arquivo = new Arquivo();
var ex = Assert.Throws(() =>
arquivo.EnviaEmail("email.com.br"));
Assert.Equal("E-mail inválido", ex.Message);
}
[Fact]
public void Testa_Envio_Email()
{
var arquivo = new Arquivo();
var ex = Assert.Throws(() =>
arquivo.EnviaEmail("email@dominio.com.br"));
Assert.Null(ex);
}
Tanto a forma Side Effect quanto a forma Exceptions podem (e devem) ser evoluídas. O que escrevi aqui foi só um mero exemplo para entendermos o conceito. A partir daqui podemos escrever testes bem mais detalhados que validam ou invalidam um método void, independente se ele altera estado ou não.
Os exemplos mostrados neste artigo estão no meu GitHub