Compartilhar via


Escrevendo testes de unidade para C/C++ com o Microsoft Unit Testing Framework para C++

No Visual Studio, você pode criar testes de unidade para código não gerenciado escrito em C++.Código não gerenciado é às vezes chamado de código nativo.

O procedimento a seguir contém as informações essenciais que é iniciado.As seções posteriores fornecem uma explicação passo a passo que descreve as etapas mais detalhadamente.

Para escrever testes de unidade para uma DLL de código não gerenciado

  1. Use o o projeto de teste nativo modelo para criar um projeto do Visual Studio separado para seus testes.

    O projeto contém alguns exemplos de código de teste.

  2. Disponibilize a DLL ao projeto de teste:

    • #includeum .h arquivo que contém as declarações das funções da DLL acessíveis externamente.

      O .h arquivo deve conter declarações de função marcadas com _declspec(dllimport).Como alternativa, você pode exportar os métodos usando um arquivo de definição.Para obter mais informações, consulte Importando e exportando.

      Os testes de unidade podem acessar somente funções que são exportadas a partir da DLL em teste.

    • Adicione o projeto DLL às referências do projeto de teste:

      No propriedades do projeto de teste, expanda propriedades comuns, estrutura e referênciase escolha adicionar referência.

  3. No projeto de teste, crie classes de teste e métodos de teste usando as macros de teste e a classe Assert da seguinte maneira:

    #include "stdafx.h"
    #include <CppUnitTest.h>
    #include "..\MyProjectUnderTest\MyCodeUnderTest.h"
    using namespace Microsoft::VisualStudio::CppUnitTestFramework;
    TEST_CLASS(TestClassName)
    {
    public:
      TEST_METHOD(TestMethodName)
      {
        // Run a function under test here.
        Assert::AreEqual(expectedValue, actualValue, L"message", LINE_INFO());
      }
    }
    
    • Assertcontém várias funções estáticas que você pode usar para verificar o resultado de um teste.

    • O LINE_INFO() parâmetro é opcional.Em casos onde não há nenhum arquivo PDB, ele permite que o test runner identificar o local de uma falha.

    • Você também pode escrever métodos de instalação e limpeza de teste.Para obter mais informações, abra a definição do TEST_METHOD macro, e leia os comentários no CppUnitTest.h

    • Você não pode aninhar classes de teste.

  4. Use o Gerenciador de testes para executar os testes:

    1. Sobre o exibição menu, escolha outras janelas, Gerenciador de testes.

    2. Compile a solução do Visual Studio.

    3. No Gerenciador de Testes, escolha Executar Tudo.

    4. Para investigar qualquer teste em mais detalhes no Gerenciador de testes:

      1. Selecione o nome do teste para ver mais detalhes, como um falha na mensagem e rastreamento de pilha.

      2. Abra o nome do teste (por exemplo, duas vezes) para ir até o local da falha ou para o código de teste.

      3. No menu de atalho para um teste, escolha depurar selecionado testar para executar o teste no depurador.

Passo a passo: Desenvolvendo uma DLL não gerenciada com o Gerenciador de testes

Você pode adaptar este passo a passo para desenvolver sua própria DLL.As etapas principais são os seguintes:

  1. Criar um projeto de teste nativo.Os testes são criados em um projeto separado da DLL que você está desenvolvendo.

  2. Criar um projeto de DLL.Este passo a passo cria uma nova DLL, mas o procedimento para testar uma DLL existente é semelhante.

  3. Tornar as funções DLL visível para os testes.

  4. Iterativamente multiplicar os testes.É recomendável um ciclo "vermelho-verde-refatorar", em que o desenvolvimento do código é organizado pelos testes.

  5. Depurar testes com falha.Você pode executar testes no modo de depuração.

  6. Refatorar enquanto mantém os testes inalterado.Refatoração significa melhorando a estrutura do código sem alterar seu comportamento externo.Você pode fazer para melhorar o desempenho, extensibilidade ou a legibilidade do código.Como a intenção não é alterar o comportamento, não altere os testes ao fazer uma alteração de refatoração do código.Os testes de ajudam a garantir que não apresenta erros enquanto você estiver refatorando.Portanto, você pode fazer essas alterações com muito mais confiança que se você não tem os testes.

  7. Verificar a cobertura de.Testes de unidade são mais úteis quando eles testam mais de seu código.Você pode descobrir quais partes do seu código foram usados pelos testes.

  8. Isolar unidades dos recursos externos.Normalmente, uma DLL é dependente de outros componentes do sistema que você está desenvolvendo, como outras DLLs, bancos de dados ou subsistemas remotos.É útil testar cada unidade em isolamento de suas dependências.Componentes externos podem fazer testes sejam executados lentamente.Durante o desenvolvimento, os outros componentes não podem ser concluídos.

Crie um projeto de teste de unidade nativa

  1. Sobre o arquivo menu, escolha novo, projeto.

    Na caixa de diálogo, expanda instalados, modelos, Visual C++, teste.

    Escolha o o projeto de teste nativo modelo.

    Neste passo a passo, o projeto de teste é denominado NativeRooterTest.

    Criar um projeto de teste de unidade em C++

  2. No novo projeto, inspecionarunittest1.cpp

    Projeto de teste com TEST_CLASS e TEST_METHOD

    Observe que:

    • Cada teste é definido usando TEST_METHOD(YourTestName){...}.

      Você não precisa gravar uma assinatura de função convencional.A assinatura é criada pela macro TEST_METHOD.A macro gera uma função de instância que retorna void.Ele também gera uma função estática que retorna informações sobre o método de teste.Essas informações permitem que o Gerenciador de testes localizar o método.

    • Métodos de teste são agrupados em classes usando TEST_CLASS(YourClassName){...}.

      Quando os testes são executados, uma instância de cada classe de teste é criada.Os métodos de teste são chamados em uma ordem não especificada.Você pode definir métodos especiais que são chamados antes e depois de cada módulo, classe ou método.Para obter mais informações, consulte organizar testes de C++.

  3. Verificar se os testes são executados no Gerenciador de testes:

    1. Insira um código de teste:

      TEST_METHOD(TestMethod1)
      {
      Assert::AreEqual(1,1);
      }
      

      Observe que o Assert classe fornece vários métodos estáticos que você pode usar para verificar os resultados em métodos de teste.

    2. No menu Testar, escolha Executar, Todos os Testes.

      O teste compilado e executado.

      O Gerenciador de testes é exibida.

      O teste aparece sob testes aprovados.

      Gerenciador de testes de unidade com um passou no teste

Criar um projeto de DLL não gerenciada

  1. Criar um Visual C++ projeto usando o Win32 Project modelo.

    Neste passo a passo, o projeto é denominado RootFinder.

    Criando um projeto de C++ Win32

  2. Selecione DLL e exportar símbolos no Assistente do aplicativo Win32.

    O exportar símbolos opção gera uma macro conveniente que você pode usar para declarar métodos exportados.

    Assistente de projeto C++ definido para a DLL e exportar símbolos

  3. Declare uma função exportada no arquivo. h principal:

    Novo código projeto e. h arquivo DLL com macros de API

    O declarador __declspec(dllexport) faz com que os membros públicos e protegidos da classe seja visível fora da DLL.Para obter mais informações, consulte Usando dllimport e dllexport em classes C++.

  4. No arquivo. cpp principal, adicione um mínimo de corpo para a função:

    // Find the square root of a number.
    double CRootFinder::SquareRoot(double v)
    {
      return 0.0;
    }
    

Acoplar o projeto de teste para o projeto DLL

  1. Adicione o projeto DLL às referências do projeto do projeto de teste:

    1. Abra as propriedades do projeto de teste e escolha propriedades comuns, estrutura e referências.

      Propriedades de projeto C++ - estrutura e referências

    2. Escolha adicionar nova referência.

      No adicionar referência caixa de diálogo caixa, selecione o projeto DLL e escolha Add.

      Propriedades de projeto C++ - adicionar nova referência

  2. No arquivo. cpp do teste de unidade principal, inclua o arquivo. h do código de DLL:

    #include "..\RootFinder\RootFinder.h"
    
  3. Adicione um teste básico que usa a função exportada:

    TEST_METHOD(BasicTest)
    {
    CRootFinder rooter;
    Assert::AreEqual(
    // Expected value:
    0.0, 
    // Actual value:
    rooter.SquareRoot(0.0), 
    // Tolerance:
    0.01,
    // Message:
    L"Basic test failed",
    // Line number - used if there is no PDB file:
    LINE_INFO());
    }
    
  4. Crie a solução.

    O novo teste aparece no Gerenciador de testes.

  5. No Gerenciador de Testes, escolha Executar Tudo.

    Gerenciador de testes de unidade - teste básico passado

Você configurar o teste e os projetos de código e verificar que você pode executar testes que executam funções no projeto de código.Agora você pode começar a escrever testes e códigos reais.

Multiplicar os testes iterativamente e fazê-los passar

  1. Adicione um novo teste:

    TEST_METHOD(RangeTest)
    {
      CRootFinder rooter;
      for (double v = 1e-6; v < 1e6; v = v * 3.2)
      {
        double actual = rooter.SquareRoot(v*v);
        Assert::AreEqual(v, actual, v/1000);
      }
    }
    
    DicaDica

    É recomendável que você não altere os testes passaram.Em vez disso, adicione um novo teste, atualize o código para que o teste seja aprovado e adicione outro teste, e assim por diante.

    Quando os usuários alterarem suas necessidades, desabilite os testes que não estão mais corretos.Escreva novos testes e fazê-los funcionar um de cada vez, da mesma maneira incremental.

  2. Compile a solução e, em seguida, no Gerenciador de testes, escolha executar todos.

    O novo teste falha.

    A falha de RangeTest

    DicaDica

    Verifique se que cada teste falhará imediatamente depois que você escreveu.Isso ajuda a evitar a facilidade de errar ao escrever um teste que nunca falha.

  3. Aprimore o código em teste para que o novo teste seja aprovado:

    #include <math.h>
    ...
    double CRootFinder::SquareRoot(double v)
    {
      double result = v;
      double diff = v;
      while (diff > result/1000)
      {
        double oldResult = result;
        result = result - (result*result - v)/(2*result);
        diff = abs (oldResult - result);
      }
      return result;
    }
    
  4. Compile a solução e, em seguida, no Gerenciador de testes, escolha executar todos.

    Ambos os testes foram bem-sucedidos.

    Aprovação do Gerenciador de testes de unidade - teste de intervalo

    DicaDica

    Desenvolva código adicionando testes, um por vez.Certifique-se de que todos os testes passaram após cada iteração.

Depurar um teste com falha

  1. Adicione outro teste:

    #include <stdexcept>
    ...
    // Verify that negative inputs throw an exception.
    TEST_METHOD(NegativeRangeTest)
    {
      wchar_t message[200];
      CRootFinder rooter;
      for (double v = -0.1; v > -3.0; v = v - 0.5)
      {
        try 
        {
          // Should raise an exception:
          double result = rooter.SquareRoot(v);
    
          _swprintf(message, L"No exception for input %g", v);
          Assert::Fail(message, LINE_INFO());
        }
        catch (std::out_of_range ex)
        {
          continue; // Correct exception.
        }
        catch (...)
        {
          _swprintf(message, L"Incorrect exception for %g", v);
          Assert::Fail(message, LINE_INFO());
        }
      }
    }
    
  2. Compile a solução e escolha executar todos.

  3. Abrir ou clique duas vezes em teste com falha.

    Falha na asserção é realçada.A mensagem de falha é visível no painel de detalhes do Gerenciador de testes.

    Falha de NegativeRangeTests

  4. Para ver por que o teste falhar, percorra a função:

    1. Defina um ponto de interrupção no início da função SquareRoot.

    2. No menu de atalho do teste com falha, escolha depurar testes selecionados.

      Quando a execução for interrompida no ponto de interrupção, percorra o código.

  5. Insira o código na função que você está desenvolvendo:

    #include <stdexcept>
    ...
    double CRootFinder::SquareRoot(double v)
    {
        // Validate parameter:
        if (v < 0.0) 
        {
          throw std::out_of_range("Can't do square roots of negatives");
        }
    
  6. Todos os testes agora foram aprovados.

    Todos os testes foram aprovados

Refatorar o código sem alterar os testes

  1. Simplifique o cálculo central na função SquareRoot:

    // old code:
    //   result = result - (result*result - v)/(2*result);
    // new code:
         result = (result + v/result)/2.0;
    
  2. Compile a solução e escolha executar todos, para certificar-se de que você não introduziu um erro.

    DicaDica

    Um bom conjunto de testes de unidade permite que você não introduziu bugs quando você alterar o código de confiança.

    Manter refatoração separadas de outras alterações.

Próximas etapas

  • **Isolamento.**A maioria das DLLs são outros subsistemas, como bancos de dados e outras DLLs dependentes.Geralmente, esses outros componentes são desenvolvidos em paralelo.Para permitir que os testes de unidade para ser executada enquanto os outros componentes ainda não estão disponíveis, você precisa substituir mock ou

  • **Crie testes de verificação.**Você pode fazer testes executados no servidor de compilação da equipe em intervalos definidos.Isso garante que os erros não são introduzidos quando o trabalho de vários membros da equipe está integrado.

  • **Testes de check-in.**Você pode designar que alguns testes são executadas antes de cada membro da equipe verifica o código no controle de origem.Normalmente, esse é um subconjunto do conjunto completo de testes de verificação de compilação.

    Você também pode designar um nível mínimo de cobertura de código.

Consulte também

Tarefas

Instruções passo a passo: criando e usando uma biblioteca de vínculo dinâmico (C++)

Conceitos

Testes de unidade de aplicativos do C++ existentes com Gerenciador de Testes

Usando Microsoft.VisualStudio.TestTools.CppUnitTestFramework

Importando e exportando

Outros recursos

Uma visão geral da interoperabilidade entre código gerenciado/

Depurando código nativo