Partilhar via


Executar o teste de unidade de uma DLL do Visual C++ para aplicativos da Store

Este tópico descreve uma maneira de criar testes de unidade para uma DLL C++ para aplicativos da Windows Store usando o Visual Studio 2012 Express para Windows 8 e o Microsoft Unit Testing Framework para C++. A DLL RooterLib demonstra memórias vagas da teoria de limite do cálculo implementando uma função que calcula uma estimativa da raiz quadrada de um determinado número. A DLL deve ser incluída em um aplicativo da Windows Store que mostre ao usuário as coisas divertidas que podem ser feitas com matemática.

Dica

Os tópicos desta seção descrevem a funcionalidade do Visual Studio 2012 Express para Windows 8. O Visual Studio Ultimate, VS Premium e VS Professional fornecem recursos adicionais para teste de unidade.

  • No VS Ultimate, VS Premium e VS Professional, você pode usar qualquer estrutura de teste de unidade de código aberto ou de terceiros que tenha criado um adaptador complementar para o Gerenciador de Testes da Microsoft. Também é possível analisar e exibir informações de cobertura de código para os testes.

  • No VS Ultimate e no VS Premium, você pode executar os testes depois de cada compilação.

Para obter mais informações, consulte Verificando código usando testes de unidade na Biblioteca MSDN.

Este tópico mostra como usar testes de unidade como a primeira etapa do desenvolvimento. Nessa abordagem, primeiramente, você escreve um método de teste que verifique um comportamento específico no sistema que está sendo testado e, em seguida, escreve um código que passe no teste. Ao fazer alterações na ordem dos procedimentos a seguir, é possível reverter essa estratégia para primeiro escrever o código que deseja testar e depois escrever as unidades de teste.

Este tópico também cria uma única solução do Visual Studio e projetos separados para os testes de unidade e a DLL que você deseja testar. Também é possível incluir os testes de unidade diretamente no projeto de DLL ou criar soluções separadas para os testes de unidade e a DLL. Consulte Testes de unidade de aplicativos do C++ existentes com Gerenciador de Testes para obter dicas sobre que estrutura usar.

Neste tópico

Este tópico orienta você para as seguintes tarefas:

Criar a solução e o projeto de teste de unidade

Verificar se o testes são executados no Gerenciador de Testes

Adicionar o projeto de DLL à solução

Acoplar o projeto de teste ao projeto de dll

Multiplicar os testes iterativamente e fazê-los passar

Depurar um teste que falhou

Refatorar o código sem alterar os testes

Criar a solução e o projeto de teste de unidade

  1. No menu Arquivo, escolha Novo e Novo Projeto.

  2. Na caixa de diálogo Novo Projeto, expanda Instalado, expanda Visual C++ e escolha Windows Store. Em seguida, escolha Biblioteca de Teste de Unidade (aplicativos da Windows Store) na lista de modelos de projeto.

    Criar uma biblioteca de teste de unidade do C++

  3. Dê ao projeto o nome RooterLibTests, especifique o local, dê à solução o nome RooterLib e verifique se Criar diretório para a solução está selecionada.

    Especifique o nome do projeto e solução e local

  4. No novo projeto, abra unittest1.cpp.

    UnitTest1.cpp

    Observe que:

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

      Você não precisa gravar uma assinatura convencional de função. A assinatura é criada pela macro TEST METHOD. A macro gera uma função de instância que retorna void. Ela 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 localizem o método.

    • Os 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 invocados antes e depois de cada módulo, classe ou método. Para obter mais informações, consulte Usando Microsoft.VisualStudio.TestTools.CppUnitTestFramework na Biblioteca MSDN.

Verificar se o testes são executados no Gerenciador de Testes

  1. Insira qualquer código de teste:

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

    Observe que a classe Assert 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 e Executar Todos.

    O projeto de teste é compilado e executado. A janela Gerenciador de Testes é exibida e o teste é listado em Testes Aprovados. O painel Resumo, na parte inferior da janela, fornece mais detalhes sobre o teste selecionado.

    Gerenciador de Testes

Adicionar o projeto de DLL à solução

  1. No Gerenciador de Soluções, escolha o nome da solução. No menu de atalho, escolha Adicionar e Adicionar Novo Projeto.

    Criar o projeto RooterLib

  2. Na caixa de diálogo Adicionar Novo Projeto, selecione DLL (aplicativos da Windows Store.

  3. Adicione o seguinte código ao arquivo RooterLib.h:

    // The following ifdef block is the standard way of creating macros which make exporting 
    // from a DLL simpler. All files within this DLL are compiled with the ROOTERLIB_EXPORTS
    // symbol defined on the command line. This symbol should not be defined on any project
    // that uses this DLL. This way any other project whose source files include this file see 
    // ROOTERLIB_API functions as being imported from a DLL, whereas this DLL sees symbols
    // defined with this macro as being exported.
    #ifdef ROOTERLIB_EXPORTS
    #define ROOTERLIB_API  __declspec(dllexport)
    #else
    #define ROOTERLIB_API __declspec(dllimport)
    #endif //ROOTERLIB_EXPORTS
    
    class ROOTERLIB_API CRooterLib {
    public:
        CRooterLib(void);
        double SquareRoot(double v);
    };
    

    Os comentários explicam o bloco ifdef, não apenas ao desenvolvedor da dll, mas a qualquer pessoa que consulte a DLL no projeto. Você pode adicionar o símbolo ROOTERLIB_EXPORTS à linha de comando usando as propriedades do projeto da DLL.

    A classe CRooterLib declara um construtor e o método avaliador SqareRoot.

  4. Adicione o símbolo ROOTERLIB_EXPORTS à linha de comando.

    1. No Gerenciador de Soluções, escolha o projeto RooterLib e escolha Propriedades no menu de atalho.

      Adicionar uma definição de símbolo do pré-processador

    2. Na caixa de diálogo Página da Propriedade de RooterLib, expanda Propriedades de Configuração, expanda C++ e escolha Pré-Processador.

    3. Escolha <Editar...> na lista Definições de Pré-Processador e adicione ROOTERLIB_EXPORTS na caixa de diálogo Definições de Pré-Processador.

  5. Adicione implementações mínimas das funções declaradas. Abra RooterLib.cpp e adicione o seguinte código:

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

Acoplar o projeto de teste ao projeto de dll

  1. Adicione RooterLib ao projeto RooterLibTests.

    1. No Gerenciador de Soluções, escolha o projeto RooterLibTests e, em seguida, Referências... no menu de atalho.

    2. Na caixa de diálogo Propriedades do Projeto RooterLib, expanda Propriedades Comuns e escolha Estrutura e Referências.

    3. Escolha Adicionar Nova Referência….

    4. Na caixa de diálogo Adicionar Referência, expanda Solução e escolha Projetos. Selecione o item RouterLib.

  2. Inclua o arquivo de cabeçalho RooterLib em unittest1.cpp.

    1. Abra unittest1.cpp.

    2. Adicione esse código abaixo da linha #include "CppUnitTest.h":

      #include "..\RooterLib\RooterLib.h"
      
  3. Adicione um teste que use a função importada. Adicione o seguinte código ao unittest1.cpp:

    TEST_METHOD(BasicTest)
    {
        CRooterLib 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. Compile a solução.

    O novo teste é exibido no Gerenciador de Teste, no nó Não Executar Testes.

  5. No Gerenciador de Testes, escolha Executar Todos.

    Êxito no teste básico

Você configurou o teste e os projetos de código, além de ter verificado que 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)
    {
        CRooterLib rooter;
        for (double v = 1e-6; v < 1e6; v = v * 3.2)
        {
            double expected = v;
            double actual = rooter.SquareRoot(v*v);
            double tolerance = expected/1000;
            Assert::AreEqual(expected, actual, tolerance);
        }
    };
    

    Dica

    É recomendável não alterar testes que tenham sido aprovados. 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 os respectivos requisitos, desabilite os testes que não estejam mais corretos. Escreva novos testes e faça-os funcionar, um por vez, da mesma maneira incremental.

  2. No Gerenciador de Testes, escolha Executar Todos.

  3. O teste falhará.

    A falha de RangeTest

    Dica

    Verifique se cada teste falha imediatamente após escrevê-lo. Isso ajuda a impedir a facilidade de errar ao escrever um teste que nunca falha.

  4. Aprimore o código sob teste para que o novo teste seja aprovado. Adicione o seguinte ao RooterLib.cpp:

    #include <math.h>
    ...
    // Find the square root of a number.
    double CRooterLib::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;
    }
    
  5. Compile a solução e, no Gerenciador de Testes, escolha Executar Todos.

    Ambos os testes passarão.

Dica

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

Depurar um teste que falhou

  1. Adicione outro teste a unittest1.cpp:

    // Verify that negative inputs throw an exception.
     TEST_METHOD(NegativeRangeTest)
     {
       wchar_t message[200];
       CRooterLib rooter;
       for (double v = -0.1; v > -3.0; v = v - 0.5)
       {
         try 
         {
           // Should raise an exception:
           double result = rooter.SquareRoot(v);
    
           swprintf_s(message, L"No exception for input %g", v);
           Assert::Fail(message, LINE_INFO());
         }
         catch (std::out_of_range ex)
         {
           continue; // Correct exception.
         }
         catch (...)
         {
           swprintf_s(message, L"Incorrect exception for %g", v);
           Assert::Fail(message, LINE_INFO());
         }
       }
    };
    
  2. No Gerenciador de Testes, escolha Executar Todos.

    O teste falhará. Escolha o nome do teste no Gerenciador de Testes. A asserção com falha é realçada. A mensagem de falha fica visível no painel de detalhes do Gerenciador de Testes.

    Falha de NegativeRangeTests

  3. Para ver o motivo da falha do teste, percorra a função:

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

    3. Adicione o código ao RooterLib.cpp para capturar a exceção:

      #include <stdexcept>
      ...
      double CRooterLib::SquareRoot(double v)
      {
          //Validate the input parameter:
          if (v < 0.0) 
          {
            throw std::out_of_range("Can't do square roots of negatives");
          }
      ...
      
    1. No Gerenciador de Testes, escolha Executar Todos para testar o método corrigido e ter certeza de que você não introduziu uma regressão.

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. Escolha Executar Todos para testar o método refatorado e ter certeza de que você não introduziu uma regressão.

    Dica

    Um conjunto estável de testes de unidade aprovados garante que você não introduziu bugs quando alterou o código.

    Mantenha a refatoração separada de outras alterações.