Partilhar via


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

  1. Abra o Visual Studio.

  2. Na janela Iniciar, escolha Criar um novo projeto.

  3. 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.

  4. 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.

  5. 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);
            }
        }
    }
    
  6. Renomeie o arquivo para BankAccount.cs clicando com o botão direito do mouse e escolhendo Renomear no Gerenciador de Soluções .

  7. 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

  1. 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.

  2. 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.

  3. Dê o nome ao projeto BankTests e clique em Avançar.

  4. 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.

  5. 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.

  6. 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.

  7. 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

  1. 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.

  2. 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

  1. No menu Build, escolha Build Solution (ou pressione Ctrl + SHIFT + B).

  2. 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).

  3. 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.

  4. 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.

Test Explorer no Visual Studio 2019 mostrando o teste aprovado

Explorador de Testes no Visual Studio 2019 a mostrar teste 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:

  1. Crie um novo método de teste chamado Debit_WhenAmountIsMoreThanBalance_ShouldThrowArgumentOutOfRange.

  2. Copie o corpo do método de Debit_WhenAmountIsLessThanZero_ShouldThrowArgumentOutOfRange para o novo método.

  3. 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.

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.