Passo a passo: Criar e executar testes de unidade para código gerenciado
Este artigo orienta você pela criação, execução e personalização de uma série de testes de unidade usando a estrutura de teste de unidade da Microsoft para código gerenciado e o Gerenciador de Testes do Visual Studio. Inicie com um projeto C# que está em desenvolvimento, crie testes que exercitem seu código, execute os testes e examine os resultados. Em seguida, você altera o código do projeto e executa os testes novamente. Se você quiser uma visão geral conceitual dessas tarefas antes de passar por essas etapas, consulte Noções básicas de teste de unidade. Se você quiser gerar testes automaticamente a partir do código existente, consulte Criar stubs de método de teste de unidade a partir do código.
Criar um projeto para teste
Abra o Visual Studio.
Na tela Iniciar, selecione Criar um novo projeto.
Pesquise e selecione o modelo de projeto do Aplicativo de Console em C# para .NET e clique em Avançar.
Observação
Se não vir o modelo Aplicativo de Console, instale-o por meio da janela Criar um novo projeto. Na mensagem Não encontrou o que precisa?, escolha o link Instalar mais ferramentas e recursos. Em seguida, no Instalador do Visual Studio, escolha a carga de trabalho Desenvolvimento de área de trabalho do .NET.
Dê ao projeto o nome Bank e clique em Avançar.
Escolha a estrutura de destino recomendada ou o .NET 8 e escolha Criar.
O projeto Bank é criado e exibido no Gerenciador de Soluções com o arquivo Program.cs aberto no editor de códigos.
Observação
Se Program.cs não estiver aberto no editor, clique duas vezes no arquivo Program.cs no Gerenciador de Soluções para abri-lo.
Substitua o conteúdo do Program.cs pelo seguinte código C# que define uma classe, BankAccount:
using System; namespace BankAccountNS { /// <summary> /// Bank account demo class. /// </summary> public class BankAccount { private readonly string m_customerName; private double m_balance; private BankAccount() { } public BankAccount(string customerName, double balance) { m_customerName = customerName; m_balance = balance; } public string CustomerName { get { return m_customerName; } } public double Balance { get { return m_balance; } } public void Debit(double amount) { if (amount > m_balance) { throw new ArgumentOutOfRangeException("amount"); } if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; // intentionally incorrect code } public void Credit(double amount) { if (amount < 0) { throw new ArgumentOutOfRangeException("amount"); } m_balance += amount; } public static void Main() { BankAccount ba = new BankAccount("Mr. Bryan Walton", 11.99); ba.Credit(5.77); ba.Debit(11.22); Console.WriteLine("Current balance is ${0}", ba.Balance); } } }
Renomeie o arquivo como BankAccount.cs clicando com o botão direito do mouse e escolha Renomear na Gerenciador de Soluções.
No menu Build clique em Criar solução (ou pressione Ctrl + SHIFT + B).
Agora você tem um projeto com métodos que você pode testar. Neste artigo, os testes se concentram no método Debit
. O método Debit
é chamado quando o dinheiro é retirado de uma conta.
Criar um projeto de teste de unidade
No menu Arquivo, selecione Adicionar>Novo Projeto.
Dica
Você também pode clicar com o botão direito do mouse na solução no Gerenciador de Soluções e escolher Adicionar>Novo Projeto.
Digite test na caixa de pesquisa, selecione C# como o idioma e, em seguida, selecione o Projeto de teste de unidade do MSTest em C# para o modelo do .NET e clique em Avançar.
Observação
No Visual Studio 2019 versão 16.9, o modelo de projeto MSTest é Projeto de Teste de Unidade.
Nomeie o projeto como BankTests e clique em Avançar.
Escolha a estrutura de destino recomendada ou o .NET 8 e escolha Criar.
O projeto BankTests é adicionado à solução Bank.
No projeto BankTests adicione uma referência ao projeto Bank.
No Gerenciador de Soluções, selecione Dependências no projeto BankTests e, em seguida, escolha Adicionar Referência (ou Adicionar referência de projeto) no menu de clique com o botão direito do mouse.
Na caixa de diálogo Gerenciador de Referências, expanda Projetos, selecione Solução e, em seguida, marque o item Banco.
Selecione OK.
Criar a classe de teste
Crie uma classe de teste para verificar a classe BankAccount
. Use o arquivo UnitTest1.cs que foi gerado pelo modelo do projeto, mas dê ao arquivo e à classe nomes mais descritivos.
Renomear um arquivo e uma classe
Para renomear o arquivo, em Gerenciador de Soluções, selecione o arquivo UnitTest1.cs no projeto BankTests. No menu do clique com o botão direito, escolha Renomear (ou pressione F2) e, em seguida, renomeie o arquivo como BankAccountTests.cs.
Para renomear a classe, posicione o cursor em
UnitTest1
no editor de código, clique com o botão direito do mouse e, em seguida, escolha Renomear (ou pressione F2). Digite BankAccountTests e, em seguida, pressione Enter.
O arquivo BankAccountTests.cs agora contém o seguinte código:
// The 'using' statement for Test Tools is in GlobalUsings.cs
// using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankTests
{
[TestClass]
public class BankAccountTests
{
[TestMethod]
public void TestMethod1()
{
}
}
}
Adicionar uma instrução using
Adicione uma instrução using
à classe de teste para permitir chamadas ao projeto em teste sem usar nomes totalmente qualificados. Na parte superior do arquivo da classe, adicione:
using BankAccountNS;
Requisitos de classe de teste
Os requisitos mínimos para uma classe de teste são:
O atributo
[TestClass]
é necessário em qualquer classe que contenha métodos de teste de unidade que você queira executar no Gerenciador de Testes.Cada método de teste que você deseja reconhecer com o Gerenciador de Testes precisa ter o atributo
[TestMethod]
.
Pode haver outras classes em um projeto de teste de unidade que não têm o atributo [TestClass]
e pode haver outros métodos em classes de teste que não têm o atributo [TestMethod]
. Você pode chamar essas classes e métodos dos seus métodos de teste.
Criar o primeiro método de teste
Neste procedimento, você escreverá métodos de teste de unidade para verificar o comportamento do método Debit
da classe BankAccount
.
Há pelo menos três comportamentos que precisam ser verificados:
O método lançará um ArgumentOutOfRangeException se o valor do débito for maior que o saldo.
O método gerará um ArgumentOutOfRangeException se o valor do débito for menor que zero.
Se o valor do débito for válido, o método subtrairá o valor do débito do saldo da conta.
Dica
Você pode excluir o método TestMethod1
padrão, porque você não o usará neste passo a passo.
Para criar um método de teste
O primeiro teste verifica que um valor válido (ou seja, um que seja menor que o saldo da conta e maior que zero) retira a quantidade correta da conta. Adicione o seguinte método à classe BankAccountTests
:
[TestMethod]
public void Debit_WithValidAmount_UpdatesBalance()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 4.55;
double expected = 7.44;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
account.Debit(debitAmount);
// Assert
double actual = account.Balance;
Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly");
}
O método é simples: ele define um novo objeto BankAccount
com um saldo inicial e, em seguida, retira um valor válido. Ele usa o método Assert.AreEqual para verificar se o saldo final é conforme o esperado. Métodos como Assert.AreEqual
, Assert.IsTrue e outros são frequentemente usados em testes de unidade. Para obter mais informações conceituais sobre como escrever um teste de unidade, consulte Escrever seus testes.
Requisitos do método de teste
Um método de teste deve atender aos seguintes requisitos:
Ele está decorado com o atributo
[TestMethod]
.Ele retorna
void
.Não pode ter parâmetros.
Criar e executar o teste
No menu Build selecione Criar solução (ou pressione Ctrl + SHIFT + B).
Se o Gerenciador de Testes não estiver aberto, abra-o escolhendo Teste>Gerenciador de Testes (ou Testar>Windows>Gerenciador de Testes) na barra de menus superior (ou pressione Ctrl + E, T).
Escolha Executar Tudo para executar o teste (ou pressione Ctrl + R, V).
Durante a execução do teste, a barra de status na parte superior da janela Gerenciador de Testes fica animada. Ao final da execução de teste, a barra ficará verde se todos os métodos de teste forem aprovados ou vermelha, se algum teste falhar.
Nesse caso, o teste falha.
Selecione o método no Gerenciador de Testes para exibir os detalhes na parte inferior da janela.
Corrigir o código e executar os testes novamente
O resultado do teste contém uma mensagem que descreve a falha. Talvez seja necessário fazer uma busca detalhada para ver essa mensagem. Para o método AreEqual
, a mensagem exibe o que era esperado e o que foi realmente recebido. Você esperava que o saldo diminuísse, mas em vez disso, ele aumentou pelo valor do saque.
O teste de unidade revelou um bug: o valor do saque foi adicionado ao saldo da conta quando deveria ser subtraído.
Corrigir o bug
Para corrigir o erro, no arquivo BankAccount.cs, substitua a linha:
m_balance += amount;
por:
m_balance -= amount;
Executar o teste novamente
No Gerenciador de Testes, escolha Executar Todos para executar o teste novamente (ou pressione Ctrl + R, V). A barra verde/vermelho fica verde para indicar que o teste foi aprovado.
Usar testes de unidade para melhorar o código
Esta seção descreve como um processo iterativo de análise, desenvolvimento de testes de unidade e refatoração pode ajudá-lo a tornar seu código de produção mais robusto e eficiente.
Analisar os problemas
Você criou um método de teste para confirmar que um valor válido é deduzido corretamente no método Debit
. Agora, verifique se o método gera uma ArgumentOutOfRangeException se o valor do débito é:
- maior que o saldo ou
- menor que zero.
Criar e executar novos métodos de teste
Crie um método de teste para verificar o comportamento correto quando o valor do débito é menor que zero:
[TestMethod]
public void Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = -100.00;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act and assert
Assert.ThrowsException<System.ArgumentOutOfRangeException>(() => account.Debit(debitAmount));
}
Use o método ThrowsException para declarar que a exceção correta foi gerada. Esse método faz com que o teste falhe, a menos que uma ArgumentOutOfRangeException seja gerada. Se você modificar temporariamente o método em teste para gerar uma ApplicationException mais genérica quando o valor do débito for menor que zero, o teste se comportará corretamente, ou seja, ele falhará.
Para testar o caso quando o valor retirado é maior que o saldo, realize as seguintes etapas:
Criar um novo método de teste chamado
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Copiar o corpo do método de
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
para o novo método.Definir
debitAmount
para um número maior que o saldo.
Execute os dois testes e verifique se eles são aprovados.
Continuar a análise
O método que está sendo testado pode ser melhorado ainda mais. Com a implementação atual, não temos como saber qual condição (amount > m_balance
ou amount < 0
) levou à exceção sendo lançada durante o teste. Nós sabemos apenas que um ArgumentOutOfRangeException
foi lançado em algum lugar no método. Seria melhor se pudéssemos dizer qual condição em BankAccount.Debit
fez com que a exceção fosse lançada (amount > m_balance
ou amount < 0
) para que possamos ter certeza de que nosso método está checando corretamente seus argumentos.
Observe novamente o método em teste (BankAccount.Debit
) e veja que ambas as instruções condicionais usam um construtor ArgumentOutOfRangeException
que apenas usa o nome do argumento como parâmetro:
throw new ArgumentOutOfRangeException("amount");
Há um construtor que você pode usar que relata informações muito mais detalhadas: ArgumentOutOfRangeException(String, Object, String) inclui o nome do argumento, o valor do argumento e uma mensagem definida pelo usuário. Refatore o método em teste para usar esse construtor. Melhor ainda, use membros de tipo disponíveis publicamente para especificar os erros.
Refatorar o código em teste
Primeiro, defina duas constantes para as mensagens de erro no escopo da classe. Coloque as definições na classe em teste, BankAccount
:
public const string DebitAmountExceedsBalanceMessage = "Debit amount exceeds balance";
public const string DebitAmountLessThanZeroMessage = "Debit amount is less than zero";
Em seguida, modifique as duas instruções condicionais no método Debit
:
if (amount > m_balance)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountExceedsBalanceMessage);
}
if (amount < 0)
{
throw new System.ArgumentOutOfRangeException("amount", amount, DebitAmountLessThanZeroMessage);
}
Refatorar os métodos de teste
Refatore os métodos de teste removendo a chamada a Assert.ThrowsException. Encapsule a chamada a Debit()
em um bloco try/catch
, capture a exceção específica que é esperada e verifique a mensagem associada. O método Microsoft.VisualStudio.TestTools.UnitTesting.StringAssert.Contains fornece a capacidade de comparar duas cadeias de caracteres.
Agora, o Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
pode ser parecido com este:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
}
}
Testar, gravar e analisar novamente
Atualmente, o método de teste não manipula todos os casos que deveria. Se o método em teste, o método Debit
, não lançar um ArgumentOutOfRangeException quando o debitAmount
for maior que o saldo (ou menor que zero), o método de teste será aprovado. Esse cenário não é bom porque você deseja que o método de teste falhe se nenhuma exceção for lançada.
Esse resultado é um bug no método de teste. Para resolver o problema, adicione uma declaração Assert.Fail ao final do método de teste para lidar com o caso em que nenhuma exceção é gerada.
Uma nova execução do teste mostra que agora o teste falha se a exceção correta é capturada. O bloco catch
captura a exceção, mas o método continua sendo executado e ele falha na nova declaração Assert.Fail. Para resolver esse problema, adicione uma instrução return
após a StringAssert
no bloco catch
. Uma nova execução do teste confirma que você corrigiu o problema. A versão final de Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
é parecida com esta:
[TestMethod]
public void Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange()
{
// Arrange
double beginningBalance = 11.99;
double debitAmount = 20.0;
BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance);
// Act
try
{
account.Debit(debitAmount);
}
catch (System.ArgumentOutOfRangeException e)
{
// Assert
StringAssert.Contains(e.Message, BankAccount.DebitAmountExceedsBalanceMessage);
return;
}
Assert.Fail("The expected exception was not thrown.");
}
Conclusão
As melhorias no código de teste levaram a métodos de teste mais robustos e informativos. Porém, o mais importante é que eles também melhoraram o código em teste.
Dica
Este passo a passo usa a estrutura de teste de unidade do Microsoft para código gerenciado. O Gerenciador de Testes também pode executar testes em estruturas de teste de unidade de terceiros que têm adaptadores para o Gerenciador de Testes. Para saber mais, consulte Instalar estruturas de teste de unidade de terceiros.
Conteúdo relacionado
Para obter informações sobre como executar testes em uma linha de comando, confira Opções de linha de comando de VSTest.Console.exe.