Yan Magalhães
Front End Developer at Ingresso Rápido
Uma introdução a testes unitários e TDD
Uma introdução a testes unitários e TDD
1.
Study: Buggy software costs users, vendors nearly $60B annually
Aaah, meu código esta livre de bugs..."
São testes que visam certificar o funcionamento de cada pequena parte do nosso sistema (classes e métodos), de forma isolada.
São os testes responsáveis por garantir o funcionamento da integração entre partes do sistema. Utilizamos classes do sistema que se comunicam, e a testamos de uma única forma, garantindo assim o funcionamento entre elas.
São os testes que garantem o funcionamento do sistema, verificando as funcionalidades implantadas e garantindo que o software realmente está fazendo o que fato era esperado.
Queremos adicionar produtos em um carrinho de compras, e depois descobrir qual é o produto de maior e menor valor.
class OrderProductTest extends PHPUnit
{
    public function testSmallerAndLarguer()
    {
        $cart = new ShoppingCart();
        $cart->add(
                new Product("Celular", 900.00));
        $cart->add(
                new Product("Grill Churrasco", 550.00));
        $cart->add(
                new Product("Pen Drive", 100.00));
        
        $order = new OrderProduct();
        $order->find($carrinho);
        
        $this->assertEquals("Pen Drive", 
            $order->getSmaller()->getName());
        $this->assertEquals("Celular", 
            $order->getLarguer()->getName());
    }
}
2.
Criado por Kent Beck, o Test Driven Development é uma metodologia de desenvolvimento de software, onde primeiro escrevemos o nosso teste e a partir daí, fazemos a implementação.
Iremos receber uma string que representa os números romanos, e em seguida, retornar o seu valor em decimal.
public function testReturnISymbol()
{
    $rome = new RomeNumberConversor();
    $number = $rome->convert("I");
    $this->assertEquals(1, $number);
}class RomeNumberConversor
{
    public function convert($romeNumber)
    {
        return;
    }
}class RomeNumberConversor
{
    public function convert($romeNumber)
    {
        return 1;
    }
}O tão famoso "passinhos de bebê" nos ajuda a entender melhor o contexto do problema. Sendo assim, buscamos fazer o teste passar, da forma mais simples e mais rápida possível.
/* Criando um Novo Teste */
public function testReturnVSymbol()
{
  $rome = new RomeNumberConversor();
  $number = $rome->convert("V");
  $this->assertEquals(5, $number);
}/* Alterando a implementação */
public function convert($romeNumber)
{
   if ($romeNumber === "V") {
     return 5;
   }
   return 1;
}Processo pelo qual se realiza a reescrita do código que será utilizado para a solução final, com o objetivo de eliminar redundâncias, sem que ocorra a mudança do resultado final produzido.
/* Agora nosso código cobre os casos Fixos de conversao */
protected $fixedNumbers = array(
   "I" => 1,
   "V" => 5,
   "X" => 10,
   "L" => 50,
   "C" => 100,
   "D" => 500,
   "M" => 1000
);
public function convert($romeNumber)
{
 if (array_key_exists($romeNumber, $this->fixedNumbers)) {
            return $this->fixedNumbers[$romeNumber];
 }
 return 0;
}/* Novos testes para II, XXII */
public function testReturnsIISymbol()
{
  $rome = new RomeNumberConversor();
  $number = $rome->convert("II");
  $this->assertEquals(2, $number);
}
public function testReturnsXXIISymbol()
{
  $rome = new RomeNumberConversor();
  $number = $rome->convert("XXII");
  $this->assertEquals(22, $number);
}/* Novos testes para IX, XXIV */
public function testReturnsIXSymbol()
{
  $rome = new RomeNumberConversor();
  $number = $rome->convert("IX");
  $this->assertEquals(9, $number);
}
public function testReturnsXXIVSymbol()
{
  $rome = new RomeNumberConversor();
  $number = $rome->convert("XXIV");
  $this->assertEquals(24, $number);
}/* Implementacao final da conversao */
public function convert($romeNumber)
    {
        $sumNumbers = 0;
        $rightLast = 0;
        for ($i = strlen($romeNumber) - 1; $i >= 0; $i--) {
          $now = 0;
            $current = $romeNumber[$i];
            if (array_key_exists($current, 
                $this->fixedNumbers)) {
                $now = $this->fixedNumbers[$current];
            }
              $factor = 1;
            if ($now < $rightLast) {
                $factor = -1;
            }
            $sumNumbers += ($this->fixedNumbers[$current] 
                * $factor);
          $rightLast = $now;
        }
        return $sumNumbers;
}Permitem simular instâncias de uma classe, permitindo realizar ações nestas classes. Classes que por algum motivo ou outro, estão fora do nosso ambiente de testes, ou que não queremos utiliza-la
class Rainfall
{
    public function __construct($institute)
    {
        $this->intitute = $institute;
    }
    public function average()
    {
        $total = 0;
        for ($i=0;$i<5;$i++) {
            $total += $this->institute->getLastRainfall();
        }
        return $total/5;
    }
}class RainfallTest extends PHPUnit_Framework_TestCase
{
    public function tearDown()
    {
        Mockery::close();
    }
    public function testGetsAverageRainfall()
    {
        $institute = Mockery::mock('\Path\To\Institute');
        $institute->shouldReceive('getLastRainfall')
        ->times(5)
        ->andReturn(70, 90, 150, 20, 50);
        $raifallObj = new Rainfall($institute);
        $this->assertEquals(76, 
            $raifallObj->getLastRainfall());
        $this->assertFalse(100, 
            $raifallObj->getLastRainfall());
    }
}Artigo acadêmico de Nagappan, mostrando um estudo de caso da Microsoft e da IBM. Este estudo mostrou que em 4 produtos, o número de erros caiu entre 40 a 90%, em comparação com os mesmos produtos, que não utilizavam TDD.
3.
/yanmagale
/yaanmagale
/yaanmagalhaes
By Yan Magalhães
Palestra sobre Testes Unitários e TDD, apresentada na SETI, em Lavras, 19/09/2016