quarta-feira, 6 de janeiro de 2010

VSTS – Visual Studio Team System para Testadores – Unit Test

Após o primeiro post sobre o VSTS – Visual Studio Team System para Testadores – Introdução vamos dar continuidade falando um pouco sobre teste unitário no VSTS.

O título desse post fala que é para Testadores, mas se você é um Desenvolvedor de software e não faz Unit Test, está mais do que na hora de começar a fazê-los. Dizem que propaganda é a alma do negócio, certo? Eu digo que Qualidade é o coração do sistema. Não pense que testar é responsabilidade única e exclusiva dos testadores.

O teste unitário (Unit Test) ou de unidade é um tipo de teste que foca na verificação da menor unidade do projeto de software. É realizado o teste de uma unidade lógica, com uso de dados suficientes para se testar apenas a lógica da unidade em questão. Um exemplo em sistemas desenvolvidos em linguagens orientadas a objetos, como C# e Java, essa unidade pode ser identificada como um método, uma classe ou mesmo um objeto.

Podemos dizer que este tipo de teste foca na arquitetura do sistema e é de responsabilidade do próprio desenvolvedor criar esses testes em tempo de implementação do sistema, isto é, após codificar uma classe, por exemplo, ele planeja, codifica e executa o teste de unidade.

Em resumo, um teste unitário executa um método individualmente e compara uma saída conhecida após o processamento da mesma.

Considero o teste unitário o principal teste na cadeia de teste e que vai garantir que o código que foi desenvolvido para o sistema em questão tenha “qualidade”. Claro que todos os outros testes da cadeia são extremamente importante, mas como o teste unitário está no início da cadeia, devemos fazer a coisa certa desde o início.

Para vocês verificarem como o teste unitário é importante, basta olhar no Modelo V de desenvolvimento, esse teste é o primeiro da cadeia.

Modelo V

Se observamos o Agile Testing Quadrants [1] iremos constatar que o Q1, ou seja, primeiro quadrante tem o nosso bom e velho amigo Teste Unitário.

Agile Testing Quadrants

  • Q1 – São os testes que focam na arquitetura. A responsabilidade é dos desenvolvedores e os analistas de teste auxiliam na elaboração dos testes unitários automáticos
  • Q2 – São os testes que focam no negócio. A responsabilidade é dos analistas de teste em conjunto com outros envolvidos no projeto(clientes, usuários, etc..). Ajuda no entendimento das funcionalidades
  • Q3 – São os testes que focam no negócio e encontrar defeitos. A responsabilidade é dos analistas de teste
  • Q4 – São os testes que focam na arquitetura, estrutura do software. A responsabilidade é dos analistas de teste

Bem, agora que já falamos um pouco sobre teste unitário vamos colocar a mão na massa. Para isso, vamos criar um novo projeto Class Library como exemplo chamado Text Validation na nossa Solution. Para fazer isso bastar clicar com o botão direito na Solution, acessar o menu Add / New Project. Na parte esquerda da tela selecionar a opção Visual C# como Project types e na parte direita da tela selecionar Class Library como Template. Feito isso, basta digitar o nome Text Validation no campo Name e confirmar a operação pressionando o botão OK.

A idéia é utilizar essa Class Library como o projeto que será aplicado o teste unitário.

Add New Class Library

Após a criação com sucesso, a Solution é apresentada com a Class Library criada da seguinte forma:

Class Library Text Validation Created

Agora vamos alterar o nome do arquivo Class1.cs para Text_Validation.cs. Neste caso, selecione o arquivo Class1.cs e pressione F2 que o mesmo entrará em modo de edição do nome do arquivo e digite Text_Validation.cs. Pronto, já estamos preparados para inserir o código da nossa Classe.

Nessa classe teremos 2 métodos:

  • GetDelimitator(): Retorna o delimitador de string utilizado na aplicação;
  • isEmail(string pStrEmail): Retorna sucesso (true) se o email informado para validação for um email com o formato válido.

Abaixo segue o código da classe que iremos utilizar como exemplo para o nosso teste unitário.

using System.Text.RegularExpressions;

namespace Text_Validation

{

    public class Text

    {

        /// <summary>

        /// Retorna o delimitador de string utilizado na aplicação

        /// </summary>

        public char GetDelimitator()

        {

            return ';';

        }

 

        /// <summary>

        /// Retorna sucesso (true) se o email informado para validação for um email com o formato válido

        /// </summary>

        /// <param name="pStrEmail">Email alvo para validação de formato</param>

        public bool isEmail(string pStrEmail)

        {

            string strRegex = @"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +

                  @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +

                  @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";

            Regex re = new Regex(strRegex);

            if (re.IsMatch(pStrEmail))

            {

                return true;

            }

            return false;

        }

    }

}

O próximo passo após a criação da nossa classe de exemplo para os testes unitários é criar o projeto de Teste. A forma mais fácil que recomendo para criar o projeto de teste é a seguinte:

  • Abrir a classe que deseja criar os testes unitários;
  • Clicar com o botão direito em qualquer lugar do código, aparecerá um menu;
  • Clicar na opção Create Unit Tests…;
  • A tela abaixo será aberta com a lista de todos os métodos da classe;

Create Unit Test Current Selection

  • Selecione os métodos que deseja criar os testes unitários. No nosso exemplo iremos habilitar todas as checkboxes;
  • No campo Output project, selecione a opção Create a new Visual C# test project… e confirme a operação selecionando o botão OK;
  • Após confirmar a operação, uma dialog será mostrada para o usuário inserir o nome do projeto no campo Enter a name for your new project. Neste campo entre com o nome Unit Test Text Validation e confirme a operação selecionando o botão Create;
  • Pronto, agora sua solution tem 2 projetos como mostrado abaixo.

Unit Test Project Created

O código gerado pelo Visual Studio deve ser alterado de acordo com a sua necessidade e cada teste que você deseja realizar. Se você abrir o arquivo TextTest.cs encontrará o seguinte código:

using Text_Validation;

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Unit_Test_Text_Validation

{

    /// <summary>

    ///This is a test class for TextTest and is intended

    ///to contain all TextTest Unit Tests

    ///</summary>

    [TestClass()]

    public class TextTest

    {

        private TestContext testContextInstance;

        /// <summary>

        ///Gets or sets the test context which provides

        ///information about and functionality for the current test run.

        ///</summary>

        public TestContext TestContext

        {

            get

            {

                return testContextInstance;

            }

            set

            {

                testContextInstance = value;

            }

        }

 

        /// <summary>

        ///A test for isEmail

        ///</summary>

        [TestMethod()]

        public void isEmailTest()

        {

            Text target = new Text(); // TODO: Initialize to an appropriate value

            string pStrEmail = string.Empty; // TODO: Initialize to an appropriate value

            bool expected = false; // TODO: Initialize to an appropriate value

            bool actual;

            actual = target.isEmail(pStrEmail);

            Assert.AreEqual(expected, actual);

            Assert.Inconclusive("Verify the correctness of this test method.");

        }

 

        /// <summary>

        ///A test for GetDelimitator

        ///</summary>

        [TestMethod()]

        public void GetDelimitatorTest()

        {

            Text target = new Text(); // TODO: Initialize to an appropriate value

            char expected = '\0'; // TODO: Initialize to an appropriate value

            char actual;

            actual = target.GetDelimitator();

            Assert.AreEqual(expected, actual);

            Assert.Inconclusive("Verify the correctness of this test method.");

        }

 

        /// <summary>

        ///A test for Text Constructor

        ///</summary>

        [TestMethod()]

        public void TextConstructorTest()

        {

            Text target = new Text();

            Assert.Inconclusive("TODO: Implement code to verify target");

        }

    }

}

Se observarmos a classe unit test acima, facilmente identificamos suas diferenças se compararmos com uma classe básica (como no exemplo da classe Text que criamos no projeto Text Validation). A classe possui o atributo [TestClass], identificando que é uma classe de testes e cada método também possui um atributo [TestMethod] identificando o método como sendo de testes. [2]

Outro ponto importante é observar que o namespace (using Microsoft.VisualStudio.TestTools.UnitTesting; ) importado na nossa classe de teste é utilizado para disponibilizar diversas funcionalidades de testes que podem ser usadas na classe do seu unit test. Esse namespace disponibiliza também a classe Assert, que dispõe de diversos métodos estáticos para validação e execução dos testes. O método AreEqual, por exemplo, em uma de suas sobrecargas, recebe por parâmetro o valor esperado da execução do teste e o valor que será enviado para validação de acordo com o método chamado como exemplo abaixo:

[TestMethod()]

        public void GetDelimitatorTest()

        {

            Text target = new Text(); // TODO: Initialize to an appropriate value

            char expected = '\0'; // TODO: Initialize to an appropriate value

            char actual;

            actual = target.GetDelimitator();

            Assert.AreEqual(expected, actual);

            Assert.Inconclusive("Verify the correctness of this test method.");

        }

Além da criação das classes de teste, ainda é possível executá-las, saber o resultado e armazená-las para comparação posterior. Agora vamos executar todos os testes criados sem alterar uma linha de código para ver qual será o resultado. Para isso, acesse o menu Test / Windows / Test List Editor. Em seguida selecione todos os testes habilitando o checkbox ao lado esquerdo do teste e click na opção Run Checked Tests como mostrado na imagem abaixo.

Run Checked Tests

Após a execução dos testes você irá perceber que os testes não tiveram seu resultado como Passed, afinal, não fizemos nenhuma alteração do código e isso era o esperado. Neste caso, tivemos 2 testes com o resultado Inconclusive e 1 com o resultado Failed como podemos observar o resultado da nossa execução abaixo.

Test Executed No Changes

Como o nosso objetivo é ter todos os casos de teste unitário com o resultados Passed, vamos fazer as devidas alterações para testar o que queremos.

Primeiro, vamos alterar o TestMethod isEmailTest(). O objetivo do nosso teste é garantir que o método isEmail(string pStrEmail) da classe Text do nosso projeto Text Validation retorna true se o email informado para validação for um email com o formato válido. Para isso devemos fazer a seguinte alteração no nosso código:

        [TestMethod()]

        public void isEmailTest()

        {

            Text target = new Text();

            string pStrEmail = "teste@teste.com.br";

            bool expected = true;

            bool actual;

            actual = target.isEmail(pStrEmail);

            Assert.AreEqual(expected, actual);

        }

Segundo, vamos alterar o TestMethod GetDelimitatorTest(). O objetivo do nosso teste é garantir que o método getDelimitator() da classe Text do nosso projeto Text Validation retorna o delimitador de string utilizado na aplicação, que neste caso é o valor ';'. Para isso devemos fazer a seguinte alteração no nosso código:

        [TestMethod()]

        public void GetDelimitatorTest()

        {

            Text target = new Text();

            char expected = ';';

            char actual;

            actual = target.GetDelimitator();

            Assert.AreEqual(expected, actual);

        }

Por último, vamos alterar o TestMethod TextConstructorTest(). Neste caso, o nosso objetivo é simplesmente garantir que conseguimos criar um objetivo do tipo Text não nulo. Para isso devemos fazer a seguinte alteração no nosso código:

        [TestMethod()]

        public void TextConstructorTest()

        {

            Text target = new Text();

            Assert.IsNotNull(target);

        }

Pronto, agora é mandar executar os testes e verificar o resultado :-). Se tudo deu certo, todos os seus testes estão com o status Passed como na imagem abaixo:

Test Executed Passed

Para quem não percebeu, todos os métodos de teste que tinham o método Assert.Inconclusive foram removidos, mas por que isso aconteceu?

O método Assert.Inconclusive indica que uma declaração não pode ser comprovado verdadeiro ou falso ou para indicar uma declaração que ainda não foi implementada. Por isso que depois de implementar todas as nossas alterações, removemos esse método. Segue a lista de sobrecarga para este caso:

Nome

Descrição

Inconclusive() Indica que a declaração não pode ser verificada.
Inconclusive(String) Indica que a declaração não pode ser verificada. Exibe uma mensagem.
Inconclusive(String, Object[]) Indica que a declaração não pode ser verificada. Exibe uma mensagem e aplica a formatação especificada pra ele.

Espero que tenham gostado!

Até+,

Quezada

Referências:

  1. Agile Testing: A Practical Guide for Testers and Agile Teams by Lisa Crispin and Janet Gregory
  2. Visual Studio Team System Rocks by Alércio Bressano, Alexandre Tarifa, Andrey Sanches, Clementino Mendonça, Emerson Facunte, Fábio Câmara, Fábio Hirota, Hélio Sá Moreira, Igor Abade V. Leite e Mauro Sant’Anna

4 comentários:

  1. Caraca, Quezada! Animal! Muito bom o artigo, e bem completo! Parabéns.

    ResponderExcluir
  2. Primeiramente parabéns!

    Com todo respeito aos que focam a carreira em áreas não técnicas, mas ver um artigo como esse faz o paradigma do elevador inverter! Parabéns!

    Há poucos anos atrás, mais ou menos 3 anos, Casos de Teste eram "cabeça de bacalhau". Todo mundo falava, montava templates, falava que usava mas raramente víamos um.

    Apesar de muitas empresas ainda não usarem CTs, já é algo mais comum no nosso dia a dia, mas hoje temos nossas novas "cabeças de bacalhau", e eu acho que uma das maiores é o teste unitário.

    Por isso, artigos como esse são cada vez mais importantes, para ajudar nossos analistas de teste a buscarem mais conhecimento técnico e qualificação em ferramentas.

    Parabéns novamente :)

    Camilo Ribeiro
    blog.camiloribeiro.com

    ResponderExcluir
  3. Sabemos que o teste unitário é o nos dá melhor retorno de investimento, sendo a forma mais "barata" de identificar um defeito. Entretanto, creio que pela nossa cultura brasileira, onde o que vale é ser "esperto" queremos é entregar o software o mais rápido possível, e depois corrigir os defeitos em produção.
    Viva o teste unitário!
    Sidney Galeote
    blog.prasabermais.com

    ResponderExcluir