Testes de unidade… qualidade, qualidade, qualidade!

Quando desenvolvemos uma aplicação, uma coisa não pode ficar de fora: QUALIDADE!

Qualidade não é uma coisa que pode ser negociada, muito menos negligenciada. Um dos fatores que indicam a qualidade de software (na minha opinião) é se os processamentos realizados pela aplicação funciona corretamente.

Mas como podemos garantir que os tais processamentos estão funcionando? A resposta é simples: TESTANDO.
Daí vem a famosa frase: “mas eu já testo! Entro em cada tela e vejo se as coisas estão funcionando corretamente”.

O problema

Será que isso é suficiente? Será que fazendo isso você tem precisão nos resultados?

Tem gente por aí que critica radicalmente este tipo de teste, dizendo que você acaba se passando por um macaco de testes, que apenas clica nos botões e verifica as mensagens.

Minha visão é prática: este tipo de teste é péssimo, simplesmente por depender de uma pessoa que executa manualmente os testes, e como todos sabemos somos falíveis (podemos esquecer de algo) e com certeza o usuário vai arranjar uma maneira de enviar algo que passe no seu teste de cliques.

A solução

Por tais motivos nós desenvolvedores devemos escrever testes automáticos para nossos códigos. Estes testes automáticos são chamados testes de unidade (ou testes unitários), e tem como objetivo verificar cada possibilidade existente nos métodos das nossas classes.

Na teoria parece simples e fácil, mas até para codificarmos testes existem alguns padrões e recomendações. Pelas minhas pesquisas vi estas:

  1. Testes DEVEM testar algo, não apenas executar (óbvio né?);
  2. Cada teste deve testar um caso do método;
  3. Testes de unidade NÃO devem depender de: banco de dados, files system ou conexão com internet;
  4. Cuidado com métodos privados;
  5. Cuidado com Singletons e métodos estáticos;
  6. Suas classes e métodos devem ser pequenas

Vamos explicar então…

Testes DEVEM testar algo, não apenas executar

Este é um tanto óbvio, você cria o teste com o objetivo de verificar automaticamente se a unidade do seu código está correta… Se você usa os testes apenas como uma forma de EXECUTAR seus métodos eu preciso muito te falar: você ta fazendo isso muito, mas muito, errado =P

Cada teste deve testar um caso do método

A idéia aqui é analisar as possibilidades do seu método e testar cada uma separado… por exemplo:

class Calculadora
{
    public function divide($dividendo, $divisor)
    {
        if ($divisor == 0) {
            throw new InvalidArgumentException('Não pode ser realizada uma divisão por ZERO');
        }

        return $dividendo / $divisor;
    }
}

Bem simples o método Calculadora::divide(), quais as possibilidades dele?

  1. Erro quando $divisor igual à ZERO
  2. Resultado da divisão de $dividendo por $divisor

Devemos então criar dois métodos de teste, um que verificará se o erro está sendo lançado e o outro pra verficar se está dividindo corretamente.

class CalculadoraTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testDivisorNaoPodeSerZero()
    {
        $calculadora = new Calculadora();
        $calculadora->divide(5, 0);
    }

    public function testDivisaoDeveRetornarResultadoCorreto()
    {
        $calculadora = new Calculadora();

        $this->assertEquals(2.5, $calculadora->divide(5, 2));
    }
}

Tranquilis né?

Testes de unidade NÃO devem depender de: banco de dados, files system ou conexão com internet

A idéia dos testes de unidade é rodar rápido a qualquer momento e em qualquer máquina (cada método de teste rodar em menos de 1 segundo), sem contar que não podemos depender da situação de um banco de dados (registros podem ser criados/removidos).

Aqui devemos retomar o objetivo geral dos testes de unidade: testar se cada possibilidade de cada método está funcionando corretamente.

Para fazer isso precisamos criar situações hipotéticas através de objetos falsos (Mocks), para que isolemos as coisas que podem ser alteradas com o passar do tempo e/ou demorar para se obter respostas.

Existe como testar banco de dados?? Sim, mas este é um outro tipo de teste (e tem suas peculiaridades =D)

Cuidado com métodos privados

Métodos privados não podem ser sobrecarregados pelas classes que extendem e os mocks nada mais são extensões da sua classe, portanto se você quiser utilizar mocks e fazer stubs para seus métodos eles deverão ser public/protected.

Outra coisa que dificultava antes do PHP 5.3.2 era testar métodos protected/private (não era possível executar somente o método), porém agora temos o ReflectionMethod::setAccessible()

Cuidado com Singletons e métodos estáticos

Testar métodos estáticos não é o problema, a questão é testar métodos que utilizam métodos estáticos e como tais métodos não dependem de instância, não conseguimos fazer stubs deles, ou seja: nada de falsear o comportamento… Uma possível solução xunxo é criar um método (protected e não estático) separado na classe que você está testando e ele apenas chama e retorna o resultado do método estático, dessa forma você consegue fazer stub.

Para Singletons o negócio é um pouco diferente. Como sabemos, a idéia dos singletons é criar uma forma única de instânciar a classe (criando apenas UMA instância). Cada método de teste deve possuir seu próprio “ambiente” e um método de teste não deve interferir em outro (a não ser que um dependa do outro). Para isso podemos fazer:

class SingletonExample
{
    private static $instance;

    public static function getInstance()
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public static function resetInstance()
    {
        self::$instance = null;
    }

    private function __construct()
    {
    }
}

Este método SingletonExample::resetInstance() serve apenas para nossos testes e ele deverá ser utilizado nos métodos tearDown() ou setUp() da nossa classe de teste, assim para cada método de teste será criado uma nova instância da classe SingletonExample.

Suas classes e métodos devem ser pequenas

Esta é obviamente uma recomendação geral para quando desenvolvemos OOP. Como um método representa UMA ação/comportamento do objeto, ele não deve ser grande (10~15 linhas) e com isso você terá classes pequenas (elas sendo bem dividas e talz não deverão ter mais de 130 linhas não comentadas) .

Essa parte é mais complicada de fazer quando estamos começando com orientação à objetos, pois temos tendencia desenvolver classes que querem dominar o mundo, mas é uma questão de tentar ver as coisas mais isoladas.

É isso ae povo, foi um pequeno livro, mas espero que tenha sido útil!

Anúncios

Tags:, , ,

About lcobucci

Passionate PHP Developer

3 responses to “Testes de unidade… qualidade, qualidade, qualidade!”

  1. jean says :

    Very muito good mano!!

    Muito bom seu post! Vou espalhar por ai…
    Um dia chego lá!

    Abraço,
    Jean

  2. Hermenegildo Marin Júnior says :

    Cara, muito bom o post, más dá pano prá manga, pois teve uma abordagem superficial e prática, creio que deve ser o primeiro de muitos sobre qualidade de software. Muito bom, estamos esperando o próximo!

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: