Passo a passo: Criar e executar testes de unidade para código gerenciado
Este artigo orienta você na 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 Visual Studio Test Explorer. Você começa com um projeto C# que está em desenvolvimento, cria testes que exercitam seu código, executa os testes e examina os resultados. Em seguida, altere o código do projeto e execute novamente os testes. 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 tu quiseres gerar testes automaticamente do código existente, consulta Criar esboços de métodos de teste unitário do código.
Criar um projeto para testar
Abra o Visual Studio.
Na janela Iniciar, escolha Criar um novo projeto.
Procure e selecione o modelo de projeto C# Console App para .NET e clique em Avançar.
Observação
Caso não veja o modelo da Aplicação de Consola , pode instalá-lo a partir da janela Criar um novo projeto . Na mensagem Não encontra o que procura?, escolha o link Instalar mais ferramentas e recursos. Em seguida, no instalador do Visual Studio, escolha o de trabalho de desenvolvimento da área de trabalho .NET.
Nomeie o projeto Bancoe depois clique em Avançar.
Escolha a estrutura alvo recomendada ou .NET 8 e, em seguida, escolha Criar.
O projeto Bank é criado e exibido em Solution Explorer com o arquivo Program.cs aberto no editor de código.
Observação
Se Program.cs não estiver aberto no editor, clique duas vezes no ficheiro 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 para BankAccount.cs clicando com o botão direito do mouse e escolhendo Renomear no Gerenciador de Soluções .
No menu Build, clique em Build Solution (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 em Gerenciador de Soluções e escolher Adicionar>Novo Projeto.
Digite de teste na caixa de pesquisa, selecione C# como idioma e, em seguida, selecione o modelo C# MSTest Test Project para .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.
Dê o nome ao projeto BankTests e clique em Avançar.
Selecione o framework alvo recomendado ou o .NET 8, e, em seguida, selecione Criar.
A partir do Visual Studio 2022 versão 17.10, você também pode selecionar um executor de teste. Para o executor de teste, você pode escolher VSTest ou MSTest. Para obter mais informações sobre a diferença entre os executores de teste, consulte comparação entre Microsoft.Testing.Platform e VSTest.
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 escolha Adicionar Referência (ou Adicionar Referência do Projeto) no menu do botão direito do mouse.
Na caixa de diálogo do Gestor de Referências , expanda Projetos, selecione a opção Soluçãoe, em seguida, marque o item Banco de Dados.
Escolha OK.
Criar a classe de teste
Crie uma classe de teste para verificar a classe BankAccount
. Você pode usar o arquivo UnitTest1.cs que foi gerado pelo modelo de 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 botão direito do mouse, escolha Renomear (ou pressione F2) e renomeie o arquivo para BankAccountTests.cs.
Para renomear a classe, posicione o cursor sobre
UnitTest1
no editor de códigos, clique com o botão direito do mouse e escolha Renomear (ou pressione F2). Digite BankAccountTests e 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 declaração using
Adicione uma instrução using
à classe de teste para poder chamar o projeto em teste, sem precisar usar nomes totalmente qualificados. Na parte superior do arquivo de classe, adicione:
using BankAccountNS;
Requisitos da classe de ensaio
Os requisitos mínimos para uma classe de ensaio são:
O atributo
[TestClass]
é necessário em qualquer classe que contenha métodos de teste de unidade que você deseja executar no Gerenciador de Testes.Cada método de teste que você deseja que o Test Explorer reconheça deve ter o atributo
[TestMethod]
.
Você pode ter outras classes em um projeto de teste de unidade que não têm o atributo [TestClass]
e você pode ter outros métodos em classes de teste que não têm o atributo [TestMethod]
. Pode chamar essas outras classes e métodos a partir dos seus métodos de teste.
Criar o primeiro método de teste
Neste procedimento, você escreve 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ça um ArgumentOutOfRangeException se o valor do débito for maior do que o saldo.
O método lança um ArgumentOutOfRangeException se o valor do débito for menor que zero.
Se o valor do débito for válido, o método subtrai o valor do débito do saldo da conta.
Dica
Você pode excluir o método TestMethod1
padrão, porque não o usará neste passo a passo.
Para criar um método de teste
O primeiro teste verifica se um valor válido (ou seja, menor que o saldo da conta e maior que zero) retira o valor correto da conta. Adicione o seguinte método a essa 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 configura 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 está conforme o esperado. Métodos como Assert.AreEqual
, Assert.IsTruee 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 ensaio
Um método de ensaio deve satisfazer os seguintes requisitos:
É decorado com o atributo
[TestMethod]
.Ele retorna
void
.Não pode ter parâmetros.
Compilar e executar o teste
No menu Build, escolha Build Solution (ou pressione Ctrl + SHIFT + B).
Se do Test Explorer não estiver aberto, abra-o escolhendo Test>Test Explorer (ou Test>Windows>Test Explorer) na barra de menus superior (ou pressione Ctrl + E, T).
Escolha Executar todos os para executar o teste (ou pressione Ctrl + R, V).
Enquanto o teste está em execução, a barra de status na parte superior da janela do
Test Explorer é animada. No final da execução do teste, a barra fica verde se todos os métodos de teste passarem, ou vermelha se algum dos testes falhar. Neste caso, o teste falha.
Selecione o método em Test Explorer para exibir os detalhes na parte inferior da janela.
Corrija o código e execute novamente os testes
O resultado do teste contém uma mensagem que descreve a falha. Talvez seja necessário detalhar para ver essa mensagem. Para o método AreEqual
, a mensagem exibe o que era esperado e o que foi realmente recebido. Esperava que o saldo diminuísse, mas em vez disso aumentou pelo valor do levantamento.
O teste unitário descobriu um bug: o valor do saque é 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;
com:
m_balance -= amount;
Execute novamente o teste
No Test Explorer, escolha Executar tudo para executar novamente o teste (ou pressione Ctrl + R, V). A barra vermelha/verde fica verde para indicar que o teste foi aprovado.
Use testes de unidade para melhorar seu código
Esta seção descreve como um processo iterativo de análise, desenvolvimento de teste de unidade e refatoração pode ajudá-lo a tornar seu código de produção mais robusto e eficaz.
Analise 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 lança um ArgumentOutOfRangeException se o valor do débito for um dos seguintes:
- superior ao saldo, ou
- menos de 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 for 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 afirmar que a exceção correta foi lançada. Esse método faz com que o teste falhe, a menos que um ArgumentOutOfRangeException seja lançado. Se você modificar temporariamente o método em teste para lançar um ApplicationException mais genérico quando o valor do débito for menor que zero, o teste se comportará corretamente, ou seja, falhará.
Para testar o caso em que o valor retirado é maior do que o saldo, execute as seguintes etapas:
Crie um novo método de teste chamado
Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
.Copie o corpo do método de
Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange
para o novo método.Defina o
debitAmount
para um número maior do que o saldo.
Execute os dois testes e verifique se eles são aprovados.
Continuar a análise
O método que está a ser testado pode ser melhorado. Com a implementação atual, não temos como saber qual condição (amount > m_balance
ou amount < 0
) levou a exceção a ser lançada durante o teste. Sabemos apenas que uma ArgumentOutOfRangeException
foi lançada em algum lugar no método. Seria melhor se pudéssemos dizer qual condição em BankAccount.Debit
causou o lançamento da exceção (amount > m_balance
ou amount < 0
) para que possamos ter certeza de que nosso método está verificando seus argumentos corretamente.
Observe o método que está sendo testado (BankAccount.Debit
) novamente e observe que ambas as instruções condicionais usam um construtor ArgumentOutOfRangeException
que apenas toma o nome do argumento como um parâmetro:
throw new ArgumentOutOfRangeException("amount");
Há um construtor que você pode usar que relata informações muito mais ricas: ArgumentOutOfRangeException(String, Object, String) inclui o nome do argumento, o valor do argumento e uma mensagem definida pelo usuário. Você pode refatorar o método em teste para usar esse construtor. Melhor ainda, você pode usar membros de tipo publicamente disponíveis para especificar os erros.
Refatore 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
Refatora os métodos de teste removendo a chamada para Assert.ThrowsException. Envolva a chamada para Debit()
num bloco try/catch
, capture a exceção específica 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 assim:
[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);
}
}
Retestar, reescrever e reanalisar
Atualmente, o método de teste não lida com todos os casos que deveria. Se o método em teste, o método Debit
, não conseguiu lançar um ArgumentOutOfRangeException quando o debitAmount
era maior do que a balança (ou menor que zero), o método de teste seria aprovado. Esse cenário não é bom porque você deseja que o método de teste falhe se nenhuma exceção for lançada.
Este resultado é um bug no método de teste. Para resolver o problema, adicione uma declaração de Assert.Fail no final do método de teste para lidar com o caso em que nenhuma exceção é lançada.
A repetição do teste mostra que o teste agora falha se a exceção correta for detetada. O bloco catch
captura a exceção, mas o método continua a executar-se e falha na nova assertiva Assert.Fail. Para resolver esse problema, adicione uma instrução return
após o StringAssert
no bloco catch
. A nova execução do teste confirma que você corrigiu esse problema. A versão final do Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange
tem esta aparência:
[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. Mas, mais importante, eles também melhoraram o código em teste.
Dica
Este passo a passo usa a estrutura de teste de unidade da Microsoft para código gerenciado. Explorador de Testes também pode executar testes de frameworks de teste de unidade de terceiros que tenham adaptadores para Explorador de Testes. Para obter mais informações, consulte Instalar estruturas de teste de unidade de terceiros.
Conteúdo relacionado
Para obter informações sobre como executar testes a partir de uma linha de comando, consulte VSTest.Console.exe opções de linha de comando.