Tutorial NUnit – Parte III: Implementando Testes Unitários

Implementando Testes de Unidade


Vimos até aqui, nesta série de tutoriais sobre testes de unidade com NUnit, quais são os benefícios dos testes de unidade e também como configurar seus projetos para trabalhar com testes de unidade. A parte teórica relacionada a como se trabalhar com testes de unidade portanto foi coberta, e iremos agora focar em conceitos mais práticos, relacionados à implementação propriamente dita dos testes de unidade.

Veremos neste artigo detalhes sobre como implementar testes de unidade, como criar classes e métodos de testes, e como utilizar asserções para verificar se seu código está se comportando da maneira esperada. Além disso nós iremos também apresentar a estrutura básica comum a testes de unidade desenvolvidos em NUnit com C#, a qual se tornará bastante familiar para você à medida que você aplicar testes de unidade no desenvolvimento de seus projetos.

Visão Geral Sobre a Implementação de Testes Unitários


Quando você cria um teste de unidade a sua intenção é verificar se o código sendo testado funciona corretamente, de acordo com a forma que você espera que seu sistema se comporte nos diversos cenários diferentes nos quais ele será utilizado. O seu foco portanto deve estar em testar comportamentos de seu código, e não necessariamente métodos individuais. Isso implica que você não necessariamente terá um teste para cada método, mas sim um conjunto de testes para cada funcionalidade de seu sistema.

Por exemplo, supondo que você estivesse desenvolvendo ou dando manutenção em um sistema de caixa eletrônico, você poderia ter vários testes diferentes para testar a funcionalidade de transferência de fundos, em vez de um único método de teste que verifica todos os casos possíveis da transferência. Entre seus casos de teste você teria um teste para verificar como seu código se comporta quando o usuário tenta transferir um saldo insuficiente; outro caso de teste para verificar o que ocorre quando o usuário tenta transferir determinado valor para uma conta que não existe, outro ainda para verificar se o seu sistema responde corretamente a uma tentativa de transferência para a mesma conta da qual o dinheiro está sendo retirado, entre outros casos de teste relevantes. Você teria desta forma no mínimo três testes de unidade, testando assim vários comportamentos diferentes para o mesmo método ou funcionalidade de seu sistema, que neste exemplo é a transferência de fundos entre contas bancárias.

O código de teste de seu sistema é necessário apenas para você e para o restante da equipe de desenvolvimento de seu software. Os clientes que adquirem seu software não precisam do código de teste para executarem o sistema, o que signifa que você não faz o deploy (entrega) de seu código de teste, mas somente de seu código de produção. O código de teste deve permanecer portanto separado do código de produção. Você pode conseguir isso colocando o código de teste em um projeto separado, em sua própria assembly, como foi demonstrado na Parte II deste tutorial sobre testes de unidade com NUnit.

Quando você testa seu sistema com testes de unidade você não executa seu código de produção diretamente. Em vez disso, você executa o código de teste, o qual por sua vez irá executar o código de produção. Isso permite a você verificar se cada uma das partes isoladas de seu sistema funciona como esperado, o que lhe dará mais garantia de que o sistema irá funcionar corretamente após todas essas funcionalidades isoladas serem integradas no produto final. Mais detalhes sobre os benefícios dos testes de unidade na Parte I deste tutorial.

Existe uma certa “fórmula” a ser seguida quando você implementa um teste de unidade para seu código. Geralmente você irá seguir o seguinte conjunto de passos para implementar cada um de seus testes de unidade, a qual é descrita no capítulo 3 do livro Pragmatic Unit Testing in C# with NUnit:

  1. Estabelecer todas as condições necessárias para a execução do teste. Em primeiro lugar você deve criar os objetos que serão usados pelo seu teste, definir o estado inicial desses objetos e alocar quaisquer recursos necessários para a execução de seu teste.
  2. Executar o método a ser testado. Com as condições necessárias para a execução de seu teste estabelecidas, a próxima coisa a fazer é executar o método correspondente ao trecho de código que você deseja testar. Isso permite que você analise o comportamento que o método apresentou ao ser executado pelo seu teste.
  3. Verificar se a funcionalidade testada se comportou da forma esperada. Após o método ser executado pelo seu teste você então analisa o novo estado de seu sistema para determinar se o método testado se comportou ou não da forma que ele deveria.
  4. Liberar os recursos que foram utilizados pelo teste. Após seu teste ser concluído você não mais necessita dos recursos que foram alocados para ele, e portanto eles devem ser liberados novamente para o sistema operacional para evitar o uso desnecessário de recursos. Por exemplo, se durante seu teste você precisou abrir algum arquivo específico para seu teste você deve se assegurar de fechar esse arquivo para que ele não permaneça aberto após o teste, a fim de evitar o consumo desnecessário de recursos de seu sistema operacional.

A maioria dos passos mencionados acima é bastante simples e intuitivas, com exceção do passo 3, o qual é específico para códigos de teste. A seção a seguir trata justamente sobre esse assunto e mostra como você pode, utilizando asserções, verificar se as funcionalidades sendo testadas se comportaram da forma esperada.

Asserções


Em NUnit, bem como em muitos outros frameworks de testes de unidade, nós usamos asserções para verificar situações e condições que nós esperamos que ocorram durante a execução de nossos testes de unidade.

Por exemplo, caso você estivesse testando o módulo de autenticação de seu sistema você poderia utilizar asserções para verificar se o sistema impede que um usuário acesse partes restritas de seu sistema analisando o resultado retornado pelo método de autenticação; você também poderia usar asserções para verificar se o usuário é redirecionado para a tela de login após o tempo máximo permitido de inatividade ser atingido, ou ainda para verificar se um usuário é bloqueado após três tentativas consecutivas de autenticação sem sucesso.

O framework NUnit oferece a você vários métodos de asserção, os quais podem ser utilizados para verificar praticamente todos os comportamentos esperados pelo seu programa. Veja abaixo uma lista de algumas das asserções disponíveis no framework NUnit, juntamente com uma breve descrição e um exemplo de uma situação simples na qual você poderia utilizar cada uma delas:

  • Assert.IsTrue(bool condição): espera que a condição informada seja verdadeira, falhando o teste caso seja uma condição falsa. Exemplo: você pode usar a asserção Assert.IsTrue(usuario.EstaAutenticado) para verificar se o usuário realmente se torna autenticado após a execução de uma autenticação com sucesso durante seu teste.
  • Assert.IsFalse(bool condição): espera que a condição informada seja falsa, falhando o teste caso seja uma condição verdadeira. Exemplo: você pode usar a asserção Assert.IsFalse(usuario.EstaAutenticado) para verificar se o usuário permaneceu não autenticado após uma falha de autenticação durante seu teste.
  • Assert.IsNull(object objeto): espera que o objeto informado seja null, falhando o teste caso seja uma referência não nula. Exemplo: Assert.IsNull(usuarioCorrente) poderia ser usado para se assegurar que o usuário corrente é nulo quando não existe nenhum usuário conectado ao seu sistema.
  • Assert.IsNotNull(object objeto): espera que o objeto informado seja não null, falhando o teste caso seja um objeto null. Exemplo: se você deseja assegurar que o objeto representando o usuário corrente foi criado após uma autenticação com sucesso em seu sistema voce poderia utilizar Assert.IsNotNull(usuarioCorrente).
  • Assert.AreEqual(double valorEsperado, double valorVerificado): espera que o valor verificado seja igual ao valor esperado, e falha o teste caso sejam valores diferentes. Exemplo: Após cadastrar um produto em seu sistema de vendas você pode utilizar algo semelhante a Assert.AreEqual(valorInformadoDoProduto, repositorioDeProdutos.ObtenhaProduto(codigoDoProduto).Valor) para verificar se o produto foi cadastrado com o valor correto que foi informado durante o cadastro do mesmo.
  • Assert.AreNotEqual(object valorInesperado, object valorVerificado): espera que os dois valores informados sejam diferentes, falhando o teste caso sejam valores iguais. Exemplo: você pode usar Assert.AreNotEqual(algumObjeto, algumOutroObjeto) para verificar se o método Equals() da classe desses objetos se comporta da forma desejada.

Note que esta é uma lista bastante resumida e de forma alguma é exaustiva, tendo como objetivo principal apenas tornar mais claro para você o que são asserções e como elas podem ser utilizadas de forma prática em seus testes de unidade. Porém, todas as outras asserções que você irá encontrar são muito parecidas com as que foram listadas acima, de forma que você não terá nenhuma dificuldade para utilizar asserções que não foram apresentadas aqui. Para uma lista completa de todas as asserções disponíveis no framework NUnit acesse sua documentação oficial em Classic Assert Model.

Criando Classes e Métodos de Teste


Para identificar quais são as partes de seu código que representam testes de unidade você precisa decorar suas classes e métodos de teste com os seguintes atributos fornecidos pelo framework NUnit:

  • [TestFixture]: usado para informar quais são as classes de testes de seu código. Todas as classes de teste de unidade que você cria devem ser decoradas com este atributo. Uma classe de teste (ou fixture) é geralmente usada para agrupar um conjunto comum de testes de unidade de seu sistema.
  • [Test]: usado para informar quais são os métodos de teste de unidade de seu código. Cada método decorado com o atributo [Test] se torna um caso de teste. Dentro de cada uma de suas classes de teste você terá um ou mais casos de teste, cada um representado por um método decorado com o atributo [Test].

Após decorar suas classes e métodos de teste, tudo que você precisa fazer é implementar seus testes propriamente ditos dentro de cada um desses métodos de teste, utilizando para isso qualquer lógica requerida, juntamente com as asserções que melhor se aplicam para a situação sendo testada.

Estrutura Básica de um Teste de Unidade Escrito com NUnit em C#


Para finalizar essa parte de nosso tutorial nós apresentamos abaixo um exemplo de um teste de unidade que aplica de forma prática alguns dos conceitos apresentados neste artigo.

Este exemplo simples apresenta a estrutura básica de um teste de unidade escrito com NUnit em C#. Veja como os atributos [TestFixture] e [Test] são aplicados à classe e ao método de teste, e também como a asserção foi usada para verificar o comportamento do código sendo testado.

[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.");
	}
}

Como complemento para esse exemplo seguem abaixo alguns detalhes sobre este código de teste:

  • A classe TestesCalculadora foi marcada como uma classe de testes decorando-a com o atributo [TestFixture].
  • O método Teste_Soma() também foi decorado com um atributo, neste caso o atributo [Test], indicando assim que se trata de um método de teste de unidade.
  • O código de teste utilizou a asserção Assert.AreEqual() para verificar se a soma calculada pelo sistema é igual aquela esperada para o cálculo.

Conclusão


Com as informações apresentadas até aqui nós agora estamos bem preparados para aplicar na prática os conceitos que foram abordados neste tutorial sobre testes de unidade.

Nosso estudo sobre testes unitários com NUnit e C# continua na Parte IV deste tutorial, na qual nós demonstramos o processo completo de execução de testes de unidade com NUnit, incluindo ferramentas, seleção de testes, execução dos testes, análise dos resultados obtidos e correção dos problemas encontrados.

Obrigado por ler este artigo.

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

2 comments

Participe da discussão. Sua contribuição é muito importante para nossa comunidade.