Tutorial NUnit – Parte IV: Executando Testes de Unidade

Executando Testes de Unidade Escritos em NUnit


Vimos até aqui, nesta série de tutoriais, diversos conceitos sobre testes de unidade e sobre sua implementação, tais como quais são os benefícios dos testes de unidade, como organizar seus projetos de testes e como implementar código de teste. Iremos agora abordar os aspectos dinâmicos dos testes de unidade, ou seja, os conceitos relacionados à execução propriamente dita de testes unitários.

Neste artigo nós iremos demonstrar como você pode executar testes de unidade, utilizando para isso um exemplo real de um projeto de testes unitários escrito com NUnit em C#. Veremos também algumas das opções de ferramentas de execução de testes disponíveis para você e quais são os passos essenciais que compõem o processo de execução de testes unitários, desde a seleção dos testes a serem executados até a análise dos resultados obtidos para nos dirigir à solução das falhas e erros encontrados.

Ferramentas e Processo de Execução de Testes de Unidade


Existem várias formas diferentes pelas quais você pode executar testes de unidade, cada uma delas sendo mais (ou menos) apropriadas para suas necessidades específicas, cabendo a você decidir qual ferramenta utilizar para realizar essa tarefa. Apresentamos abaixo algumas das principais formas de execução de testes unitários. Comente no final deste artigo sobre ferramentas e abordagens de execução de testes de unidade que você conhece.

  • Via linha de comando: você pode executar seus testes de unidade diretamente no prompt de comando (shell) do Windows fornecendo para o NUnit os parâmetros descritos na documentação oficial do NUnit Framework. Essa talvez seja a melhor opção caso você esteja utilizando um servidor de integração contínua para executar seus testes de unidade durante o processo de build de seu software.
  • Plugins ou Extensões: bastante útil para programadores, esta opção permite a você executar seus testes de unidade sem sair do Visual Studio ou de sua IDE preferida. Alguns desses plugins e extensões são gratuitos, tais como NUnit Test Adapter, enquanto outros são proprietários, como por exemplo o Telerik Unit Test Runner.
  • NUnit GUI: é a IDE de execução de testes nativa do NUnit Framework, a qual oferece a você uma interface gráfica com vários recursos, comandos e opções que facilitam a seleção de testes a serem executados, sua execução e a visualização dos resultados obtidos, como veremos neste artigo.

Independentemente de qual dessas abordagens ou de qual ferramenta você escolha, o processo de execução de testes unitários será basicamente o mesmo, sendo constituído por atividades semelhantes às seguintes:

  1. Selecionar os testes: a primeira coisa a fazer é escolher quais testes você deseja executar. Você pode, por exemplo, selecionar todos os seus testes ou escolher executar somente os testes relacionados à funcionalidade que você está desenvolvendo. corrigindo ou modificando.
  2. Executar os testes: a seguir você executa os testes que você selecionou.
  3. Analisar os resultados: após os testes serem executados você analisa os resultados dos testes. Algumas ferramentas apresentam os resultados diretamente em suas interfaces gráficas, enquanto outras feramentas geram arquivos contendo os resultados obtidos. Entre as informações que você pode extrair dos resultados da execução de seus testes encontram-se quais e quantos testes falharam, quais e quantos testes passaram (tiveram sucesso), detalhes sobre as falhas e erros e qual foi a duração de execução de seus testes.
  4. Corrigir os problemas encontrados: caso a execução de seus testes não apresente como resultado 100% de sucesso, você irá precisar tomar algumas ações para corrigir os testes que estão falhando ou produzindo erros. Damos um exemplo, logo a seguir, sobre como isso pode ser feito.
  5. Executar os testes novamente: após corrigir os problemas encontrados, você executa seus testes novamente para verificar se os problemas foram de fato corrigidos e se novos problemas não surgiram devido a efeitos colaterais provocados pelas suas alterações no código, iniciando assim o processo novamente e repetindo-o quantas vezes forem necessárias para remover todas as falhas e erros encontrados em seu código.

Neste artigo nós iremos seguir este processo completo, demonstrando cada um dos passos descritos acima de forma prática utilizando como exemplo a ferramenta NUnit GUI. Acreditamos, contudo, que as informações dadas aqui serão suficientes para você trabalhar com qualquer outra ferramenta de execução de testes de unidade, pois os conceitos e o processo são bastante semelhantes, de forma que você não terá problemas para aplicar essas informações em sua ferramenta de escolha.

A Ferramenta NUnit GUI


A ferramenta NUnit GUI (GUI = Interface Gráfica do Usuário) é encontrada no diretório de instalação do NUnit Framework sob o nome de nunit.exe. Após instalar o NUnit Framework (veja como fazer isso na seção “Adicionando o Framework NUnit ao Projeto de Testes Unitários” da Parte II deste tutorial) o NUnit GUI ficará disponível em uma pasta semelhante a “C:Program Files (x86)NUnit 2.6.3binnunit.exe” em seu computador. Note que esta pasta pode variar dependendo de seu sistema operacional e da versão instalada do NUnit.

Quando você executa este aplicativo (nunit.exe) a NUnit GUI se apresenta inicialmente como na figura abaixo:

Nunit IDE

A partir desta figura podemos identificarmos alguns dos recursos que esta ferramenta apresenta:

  • Dois botões entitulados Run e Stop, os quais, respectivamente, executam e param a execução dos testes de unidade.
  • Uma aba entitulada “Errors and Failures”, na qual são listados todos os erros e falhas que ocorreram durante a execução de seus testes de unidade.
  • Uma aba entitulada “Tests Not Run”, na qual são listados os testes que, apesar de terem sido selecionados para serem executados, não foram executados.
  • Uma aba entitulada “Text Output”, a qual apresenta todos os textos de log encontrados no código de teste, os quais podem ser criados utilizando os métodos System.Diagnostics.Debug.Write() e System.Console.WriteLine() em seu código de teste.
  • Uma aba entitulada “Tests”. Aqui é onde são listados todos os seus testes de unidade, como veremos um pouco mais adiante neste artigo.
  • Um label entitulado “Test Cases”, e cujo valor atualmente é 0. Este label irá informar o número de testes de unidade que foram selecionados para serem executados.

Outros recursos disponíveis na ferramenta Nunit GUI podem ser acessados pelas opções de menu da aplicação. Algumas dessas opções de menu são:

  • Menu File: este menu apresenta um conjunto de operações relacionadas a projetos de testes unitários. Aqui você pode criar um novo projeto, abrir um projeto existente ou fechar o projeto atualmente aberto. Você pode também salvar o projeto atualmente aberto, recarregar o projeto e visualizar uma lista dos projetos nos quais você trabalhou recentemente. Este menu te permite também fechar a aplicação selecionando a opção File -> Exit.
  • Menu View: apresenta operações relacionadas à apresentação da Interface Gráfica da aplicação, tais como alterar o tamanho da fonte ou ocultar e mostrar determinadas partes da Interface Gráfica, de acordo com sua preferência pessoal.
  • Menu Project: este menu permite a você modificar as configurações de seu projeto e adicionar novas Assemblies de testes para o seu projeto.
  • Menu Tets: este menu apresenta funções que lhe permitem executar e abortar a execução dos testes de unidade.
  • Menu Tools: do ponto de vista desse tutorial a opção mais importante deste menu é “Save Results as XML…”, a qual lhe permite salvar os resultados da execução de seus testes para análise futura. (As outras operações disponíveis neste menu são relacionadas ao próprio NUnit GUI, e não com seus testes de unidade, motivo pelo qual não iremos entrar em mais detalhes sobre este menu neste tutorial.)
  • Menu Help: permite a você acessar uma versão offline da docmentação oficial do NUnit, além de visualizar informações sobre a versão do NUnit GUI que você está utilizando.

Compilando o Código a Ser Testado


A primeira coisa a fazer antes de executar os testes de unidade é compilar nosso código, tanto o código de produção quanto o código de testes. Isso é necessário para que a Assembly (*.dll) de nosso projeto de testes seja gerada com as últimas modificações feitas em nosso código.

Neste artigo nós iremos utilizar uma versão modificada do projeto da Calculadora que foi apresentado na Parte II desta série de tutoriais. Portanto, caso você ainda não tenha criado esse projeto, sugerimos que você leia a Parte II desta série de tutoriais antes de prosseguir.

Abra o projeto da calculadora e modifique as classes Calculadora e TestesCalculadora para que fiquem como mostrado abaixo:

namespace CalculadoraSimples
{
	// Classe que representa nossa calculadora.
    public class Calculadora
    {
        public int Somar(int a, int b)
        {
            return a + b;
        }

        public int Subtrair(int a, int b)
        {
            return a - b;
        }

        public int Multiplicar(int a, int b)
        {
            // Falha intencional: o teste de multiplicação irá falhar pois a função está somando em vez de multiplicando.
            return a + b;
        }

        public int Dividir(int a, int b)
        {
            return a / b;
        }

        public double Exponenciacao(int a, int b)
        {
            // Falha intencional: o tete de exponenciação irá falhar pois a função está retornando sempre 0.
            return 0;
        }
    }
}
using CalculadoraSimples;
using NUnit.Framework;

namespace TestesCalculadoraSimples
{
    [TestFixture]
    public class TestesCalculadora
    {
        [Test]
        public void Teste_Soma()
        {
            // Define os valores a serem usados durante os testes.
            int parcelaA = 10;
            int parcelaB = 20;
            int somaEsperada = (parcelaA + parcelaB);

            // Cria a calculadora.
            Calculadora calculadora = new Calculadora();

            // Exercita a funcionalidade de soma da calculadora.
            int somaObtida = calculadora.Somar(parcelaA, parcelaB);

            // Verifica se a soma foi efetuada corretamente.
            Assert.AreEqual(somaEsperada, somaObtida, "A função de soma da calculadora não retornou o valor esperado.");
        }

        [Test]
        public void Teste_Subtracao()
        {
            // Define os valores a serem usados durante os testes.
            int a = 10;
            int b = 7;
            int resultadoEsperado = (a - b);

            // Cria a calculadora.
            Calculadora calculadora = new Calculadora();

            // Exercita a funcionalidade de subtração da calculadora.
            int resultadoObtido = calculadora.Subtrair(a, b);

            // Verifica se a subtração foi efetuada corretamente.
            Assert.AreEqual(resultadoEsperado, resultadoObtido, "A função de subtração da calculadora não retornou o valor esperado.");
        }

        [Test]
        public void Teste_Multiplicacao()
        {
            // Define os valores a serem usados durante os testes.
            int a = 2;
            int b = 3;
            int produtoEsperado = (a * b);

            // Cria a calculadora.
            Calculadora calculadora = new Calculadora();

            // Exercita a funcionalidade de multiplicação da calculadora.
            int produtoObtido = calculadora.Multiplicar(a, b);

            // Verifica se a multiplicação foi efetuada corretamente.
            Assert.AreEqual(produtoEsperado, produtoObtido, "A função de multiplicação da calculadora não retornou o valor esperado.");
        }

        [Test]
        public void Teste_Divisao()
        {
            // Define os valores a serem usados durante os testes.
            int a = 50;
            int b = 10;
            int resultadoEsperado = (a / b);

            // Cria a calculadora.
            Calculadora calculadora = new Calculadora();

            // Exercita a funcionalidade de divisão da calculadora.
            int resultadoObtido = calculadora.Dividir(a, b);

            // Verifica se a divisão foi efetuada corretamente.
            Assert.AreEqual(resultadoEsperado, resultadoObtido, "A função de divisão da calculadora não retornou o valor esperado.");
        }

        [Test]
        public void Teste_Exponenciacao()
        {
            Calculadora calculadora = new Calculadora();

            Assert.AreEqual(1, calculadora.Exponenciacao(5, 0), "A exponenciação da calculadora não está funcionando corretamente.");
            Assert.AreEqual(5, calculadora.Exponenciacao(5, 1), "A exponenciação da calculadora não está funcionando corretamente.");
            Assert.AreEqual(25, calculadora.Exponenciacao(5, 2), "A exponenciação da calculadora não está funcionando corretamente.");
            Assert.AreEqual(0.2, calculadora.Exponenciacao(5, -1), "A exponenciação da calculadora não está funcionando corretamente.");
        }
    }
}

Note que nós inserimos algumas falhas propositais no código por motivos didáticos. Pedimos que você mantenha o código com essas falhas, as quais iremos corrigir enquanto demonstramos a você o processo de execução de testes de unidade.

Com seu código atualizado compile os projetos CalculadoraSimples e TestesCalculadoraSimples selecionando a opção de menu Build > Build Solution (ou pressionando a tecla de atalho F6), como mostrado na figura abaixo.

Menu Build Solution

A construção de sua solução irá gerar os binários do código de seu programa no diretório bin de seus projetos. Dependendo da configuração de sua solução você pode encontrar os binários gerados ou no diretório bindebug ou no diretório binrelease, portanto certifique-se de analisar esses dois diretórios caso você não encontre as DLLs do projeto na primeira pasta em que você procurar por esses arquivos.

Carregando os Testes de Unidade no NUnit


Para executar seus testes de unidade você precisa criar um projeto de testes na ferramenta NUnit GUI e adicionar a esse projeto todas as DLLs compiladas dos seus projetos de testes de unidade. Desta forma o NUnit irá analisar seu código de teste já compilado e construído (binários) e verificar quais são as classes e métodos de teste que você definiu para testar seu código.

Siga os passos abaixo para criar o projeto de testes unitários no NUnit GUI:

  1. Na IDE do NUnit selecione a opção de menu File -> New Project.
  2. Escolha um nome para seu projeto de testes NUnit e um local no qual deseja salvar seu projeto. Clique em Salvar. A IDE do NUnit irá criar seu projeto de testes como um arquivo *.nunit na pasta que você escolheu.
  3. Selecione a opção de menu Project > Add Assembly….
  4. Menu Project Add Assembly

  5. Na caixa de diálogo Add Assembly navegue até o diretório dos binários do projeto do código de testes que nós criamos no Visual Studio (TestesCalculadoraSimples), selecione a DLL de mesmo nome do projeto (TestesCalculadoraSimples.dll) e clique em Abrir.
Adicionando uma Assembly ao projeto do NUnit.Adding assemblies to NUnit projects.

Adicionando uma Assembly ao projeto do NUnit.

Ao fazer isso o NUnit GUI irá carregar todos os testes de unidade que nós implementamos no projeto de testes da calculadora, apresentando nossos testes como uma estrutura de árvore organizada e dividida em classes e métodos de teste, como você pode ver na figura abaixo.

Testes Carregados na IDE do NUnit

Executando os Testes de Unidade


Para executar os testes de unidade, selecione o nó da árvore correspondente à nossa classe de testes da calculadora, TestesCalculadora, e clique no botão Run. Com isso o NUnit irá começar a execução dos testes e irá mostrar uma barra de progresso do andamento da execução dos testes de unidade. Ao executar os testes da calculadora você verá que a barra de progresso ficou vermelha durante e após a execução dos testes. Isso ocorreu por que existem testes que estão falhando, e a cor vermelha é um feedback para alertar o desenvolvedor de que algumas das funcionalidades sendo testadas precisam ser corrigidas.

Abaixo da barra de progresso da execução dos testes o NUnit GUI está nos informando que 3 de nossos testes passaram (foram executados com sucesso) e dois testes falharam. Podemos ver claramente, no lado esquerdo da janela do NUnit GUI, que alguns dos testes foram marcados com cor verde e outros com cor vermelha, indicando respectivamente quais são os testes que passaram e quais testes falharam. Para a versão atual do código de nossa calculadora os testes que tiveram sucesso foram os de Divisão, Soma e Subtração, enquanto que os testes de Exponenciação e Multiplicação resultaram em falha.

Resultado dos Testes Falhando

Resultado dos Testes Falhando

Como dissemos anteriormente, e como podemos ver agora, os resultados apresentados pelo NUnit GUI após a execução dos testes de unidade nos traz informações importantes sobre o código que nós estamos testando, tais como:

  • O número de casos de teste que foram executados.
  • O número de erros ocorridos durante a execução dos testes.
  • O número de testes cuja execução resultou em falha.
  • A duração total da execução dos testes de unidade
  • O número de testes que tiveram sucesso durante sua execução.
  • Uma mensagem descrevendo o motivo pelo qual cada teste falhou, a qual nós definimos no código de teste como um parâmetro nos métodos de asserções. (Asserções foram descritas na Parte III desta série de tutoriais sobre testes de unidade.)

Analisando e Corrigindo os Problemas Encontrados


Com a execução de nossos testes de unidade nós fomos capazes de identificar claramente quais são as funcionalidades que precisam ser corrigidas em nosso código. Muitas falhas como essas podem ocorrer de forma despercebida em seu código, de forma que uma suite de testes de unidade se torna essencial para que você encontre essas falhas e aumente a confiabilidade de seu código.

Para entender melhor o motivo dos testes terem falhado nós podemos analisar os resultados gerados pelo NUnit após a execução dos testes. Abaixo da barra de progresso de execução dos testes, na aba Erros and Failures, o NUnit apresenta uma lista com o nome, mensagem de erro e motivo pelo qual cada um dos testes falhou, como mostrado na imagem a seguir:

Lista com informacao sobre as falhas dos testes

Lista com informacao sobre as falhas dos testes

Analisando o primeiro ítem dessa lista, TestesCalculadoraSimples.TestesCalculadora.Teste_Exponenciacao, descobrimos que o teste de exponenciação falhou por que uma asserção esperava o valor 1, porém o valor foi verificado em 0, ou seja, o código não se comportou da forma que deveria. Continuando nossa análise da falha deste teste, clique no botão “Display actual stack trace” Actual Stack Trace para visualizar a Stack Trace correspondente à falha encontrada durante a execução do teste. Por exemplo, para a falha do teste de Exponenciação, o NUnit GUI nos apresenta uma stack trace semelhante à seguinte:


at TestesCalculadoraSimples.TestesCalculadora.Teste_Exponenciacao() in D:UlyssesDropboxBlog C# ProgrammerConteudo das PublicacoesTutoriaisTestes Unitarios com nUnitCodigoCalculadoraSimplesTestesCalculadoraSimplesTestesCalculadora.cs:line 86

Note que essa stack trace nos informa exatamente qual foi o arquivo e a linha de código na qual a falha ocorreu, como destacado em negrito no parágrafo anterior. Isso nos dá mais uma dica para facilitar a localização da origem do problema. Se abrirmos o arquivo especificado na linha informada, veremos que a falha ocorreu no seguinte ponto do código de nosso teste:

Assert.AreEqual(1, calculadora.Exponenciacao(5, 0), "A exponenciação da calculadora não está funcionando corretamente.");

Você pode também verificar a linha de falha sem sair do NUnit GUI, bastando clicar no botão “Display Source Code Context” Display Source Code Context. Ao clicar nesse botão o NUnit irá mostrar para você o contexto correspondente à falha encontrada no código fonte de seu teste, destacando em vermelho a linha de código na qual a falha ocorreu, como podemos ver na seguinte figura:

Source Code Context

Source Code Context

A falha do teste nesta linha de código nos leva à conclusão de que o método Exponenciação da classe Calculadora não calculou corretamente o valor da exponenciação de 5 elevado a 0 (5^0). Em vez de retornar 1 ( que seria o resultado correto para esse cálculo matemático) o resultado obtido foi 0, que é o que o NUnit GUI nos informou na lista de falhas dos testes com a mensagem “Expected 1 But was 0.0d”.

Isso demonstra claramente que os testes de unidade cumprem seu papel em nos permir identificar não somente quais funcionalidades de nosso software estão com problemas, mas também onde esses problemas estão localizados em nosso código fonte. Isso facilita muito as tarefas de desenvolvimento e manutenção de software, principalmente para aqueles grandes sistemas nos quais existem literalmente centenas ou mesmo milhares de linhas de código trabalhando juntas para o bom funcionamento (ou não) de seu sistema.

Para uma lista mais completa dos benefícios que os testes de unidade podem trazer para o seu processo de desenvolvimento de software leia a Parte I desta série de tutoriais sobre testes de unidade com NUnit em C# .

Corrigindo os Problemas Encontrados


Como vimos na seção anterior, a função Calculadora.Exponenciacao() não está retornando o valor esperado para os cálculos, portanto esse é o ponto de nosso código que deve ser corrigido. Se analisarmos o código deste método, veremos que ele se encontra atualmente implementado da seguinte forma:

        public double Exponenciacao(int a, int b)
        {
            // Falha intencional: o teste de exponenciação irá falhar pois a função está retornando sempre 0.
            return 0;
        }

Como vemos, este método está sempre retornando 0 e nunca executando realmente o cálculo da exponenciação dos parâmetros recebidos, sendo esse portanto o motivo da falha de nosso teste. Para corrigir o problema você pode modificar o método de exponenciação utilizando qualquer algoritmo de exponenciação que desejar, ou utilizar a própria implementação utilizada pelo .NET Framework no método System.Math.Pow() como fizemos aqui:

        public double Exponenciacao(int a, int b)
        {
            // Falha Corrigida: agora nosso código está realmente calculando a exponenciação dos valores passados como parâmetros.
            return System.Math.Pow(a, b);
        }

Seguindo esse mesmo processo nós podemos verificar também o que provocou a falha do teste de Multiplicação: o programador (talvez cansado já no final do expediente) se equivocou e utilizou o operador de adição (+) em vez do operador de multiplicação (*), obtendo assim a soma dos valores informados como parâmetro, em vez de retornar o resultado da multiplicação desses valores. Modifique o método Calculadora.Multiplicar() para corrigir essa falha, de forma que ele fique como mostrado no trecho de código abaixo:

        public int Multiplicar(int a, int b)
        {
            // Falha Corrigida: o operador soma (+) foi removido e substituído pelo operador multiplicação (*).
            return a * b;
        }

Executando os Testes Novamente


Após realizar as devidas alterações para corrigir as falhas encontradas em nosso código, nós devemos compilar nossa solução e executar os testes novamente. Isso irá nos mostrar se as modificações que fizemos de fato corrigiram os problemas encontrados durante os testes, além de também nos assegurar de que essas alterações não provocaram efeitos colaterais nas demais partes de nosso sistema.

Como nós fizemos alterações no código após a última execução dos testes, precisamos acessar a opção File -> Reload Tests (CTRL+R) para que o NUnit GUI possa recarregar a Assembly dos testes com as alterações que foram feitas. Depois disso, execute os testes novamente e você verá que a barra de progresso de execução dos testes dessa vez ficou verde, indicando que todos os testes executados passaram com sucesso, como mostrado abaixo:

Barra  De Progresso Verde - 100% de Sucesso nos testes executados!

Barra De Progresso Verde – 100% de Sucesso nos testes executados!

A barra de progresso verde nos dá um indicativo de que todas as funcionalidades que foram testadas estão funcionando da forma que deveriam, e que não há falhas ocorrendo em nosso software. Considerando que seus testes estejam corretos e que você tenha uma boa cobertura de testes para seu código, esse pode ser um bom indicativo da qualidade de seu código e, consequentimente, um bom indicativo da qualidade de seu software.

Conclusão Parte IV


Vimos nesse tutorial como você pode executar seus testes de unidade e como os resultados obtidos podem te ajudar a encontrar quais são e onde se encontram as falhas de seu código, dando a você uma forma simples, porém eficiente, de manter os bugs longe de seu software.

Esperamos que as informações apresentads aqui possam ajudá-lo(a) e incentivá-lo(a) a implementar e executar testes unitários em seus próprios projetos, bem como também a analisar os resultados obtidos para corrigir as falhas encontradas em seu código.

Na próxima parte deste tutorial, a qual será publicada em breve, nós iremos demonstrar de forma prática como construir software utilizando a técnica de Desenvolvimento Dirigido por Testes (TDD – Test Driven Development). Assine nossa Newsletter ou se inscreva em nosso Feed para ser notificado quando esse e outros artigos estiverem disponíveis em nosso blog.

Obrigado por ler esse artigo, e não se esqueça de aplicar testes de unidade em seus projetos para aumentar a qualidade de seu código.

Se você gostou dessa publicação, lembre-se de assinar nossa Newsletter e/ou se inscrever em nosso Feed para ficar atualizado sobre as futuras publicações de C# Programmer.


Leave a Reply

Your email address will not be published. Required fields are marked *