Testes de unidade
      avançados e TDD
     Luís Otávio Cobucci Oblonczyk



21 de Outubro de 2011
       6° SoLiSC
Luís Otávio Cobucci Oblonczyk
●
    Desenvolvedor PHP na Softnex Tecnologia
●
    Orientador no Senac TI
●
    Doido por PHP desde 2003
●
    Perfeccionista ao extremo =P



    @lcobucci
    http://about.me/lcobucci
Normalmente quando desenvolvemos fazemos o teste F5
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
                                        4) F5
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
                                        4) F5
                                        5) Muda o código
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
                                        4) F5
                                        5) Muda o código
                                        6) F5
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
                                        4) F5
                                        5) Muda o código
                                        6) F5
                                        …
Normalmente quando desenvolvemos fazemos o teste F5



                                        1) Abre sistema no
                                       browser
                                        2) Procura o erro
                                        3) Muda o código
                                        4) F5
                                        5) Muda o código
                                        6) F5
                                        …
                                        999) Resolve o
                                       problema
Quanto tempo PERDEMOS nesta brincadeira?
Quanto dinheiro PERDEMOS nesta brincadeira?
E a pergunta que não quer calar...
Quando isso acontece num ambiente “separado”
não tem GRANDES problemas
E quando acontece na “vida real”?
Tá, e o que eu posso fazer?
Tá, e o que eu posso fazer?




    Automatizar alguns testes!
                     (pra começar)
Testes de Unidade
 “Teste de unidade é toda a aplicação de teste
 nas assinaturas de entradas e saídas de um
 sistema, consiste em validar dados válidos e
 inválidos via I/O (entrada/saída) sendo aplicado
 por desenvolvedores ou analistas de teste.
 Uma unidade é a menor parte testável de um
 programa de computador.”


 http://pt.wikipedia.org/wiki/Teste_de_unidade
Testes de Unidade
●
    Te dá uma visão rápida do teu código: ou
    funciona ou não funciona
●
    Facilita o desenvolvimento, pois caso alguma
    alteração estrague uma funcionalidade já
    existente, você fica sabendo antes mesmo de
    ser enviado para os sistemas de controle de
    versão
●
    Servem também como parte da documentação
●
    Ajuda a manter a sanidade mental da equipe
Utilizarei aqui funcionalidades
presentes a partir do PHP 5.3.2!
Utilizarei aqui funcionalidades
presentes a partir do PHP 5.3.2!




                                   Se você ainda não usa
                                   o PHP 5.3 é uma boa
                                   oportunidade de
                                   começar ; )
PHPUnit
 O que é: Framework p/ criação e execução de
 testes de unidade. Faz parte da família xUnit.
 Criador: Sebastian Bergmann
 Instalação:
   pear config-set auto_discover 1
   pear install pear.phpunit.de/PHPUnit


 http://www.phpunit.de
Bora pro código!
src/Lcobucci/Examples/Tests/Calculator.php
<?php
namespace LcobucciExamplesTests;

class Calculator
{
    public function sum($number1, $number2)
    {
        return $number1 + $number2;
    }
}
test/Lcobucci/Examples/Tests/CalculatorTest.php
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'PHPUnit/Framework/TestCase.php';

use LcobucciExamplesTestsCalculator;

class CalculatorTest extends PHPUnit_Framework_TestCase
{
    public function testSum()
    {
        $calculator = new Calculator();
        $this->assertEquals(4, $calculator->sum(2, 2));
    }
}
TestCase e TestSuite
●
    O TestCase possui métodos de testes para um
    recurso (normalmente uma classe)
●
    TestSuites formam grupos de testes, ou seja
    dentro de um TestSuite podem ter vários
    TestCases ou até vários TestSuites
tearDown() e setUp()
●
    São métodos protegidos (com visibilidade
    protected) disponíveis tanto no TestCase
    quanto no TestSuite
●
    Podem ser sobrecarregados caso NECESSÁRIO,
    e são utilizados para preparar e limpar o
    ambiente de teste
●
    No TestCase o setUp() é executado ANTES de
    cada método de teste e o tearDown ao FINAL
    de cada método
tearDown() e setUp()
●
    No TestSuite o setUp() é executado ANTES de
    cada item da suite de testes e o tearDown ao
    FINAL de cada item
Boas práticas
●
    Não crie métodos de teste que não teste
    absolutamente nada
●
    Não queira testar várias situações em um
    método só
●
    Use os métodos corretos para comparações
    ●
        $this->assertEmpty($var); vs
        $this->assertTrue(empty($var));
    ●
        $this->assertInstanceOf('Exception', $e); vs
        $this->assertTrue($e instanceof Exception);
Boas práticas
●
     Crie um método de teste para cada condição
    de teste de um método usando nomes
    significativos
●
    Mantenha os diretórios de teste similares aos
    diretórios das classes
●
    Use arquivo de configuração com um arquivo
    de bootstrap
src/Lcobucci/Examples/Tests/Calculator.php
<?php
namespace LcobucciExamplesTests;

use InvalidArgumentException;

class Calculator
{
    public function div($dividend, $divisor)
    {
        if ($divisor == 0) {
            throw new InvalidArgumentException(
                 'The divisor cannot be ZERO'
            );
        }

          return $dividend / $divisor;
     }
}
test/Lcobucci/Examples/Tests/CalculatorTest.php
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'PHPUnit/Framework/TestCase.php';
use LcobucciExamplesTestsCalculator;

class CalculatorTest extends PHPUnit_Framework_TestCase
{
    /**
      * @expectedException InvalidArgumentException
      */
    public function testDivisorCannotBeZero()
    {
         $calculator = new Calculator();
         $calculator->div(2, 0);
    }

    public function testDivMustReturnTheRightDivision()
    {
        $calculator = new Calculator();
        $this->assertEquals(2, $calculator->div(4, 2));
    }
}
Test Doubles
●
    São objetos “falsos” que utilizamos para isolar
    elementos que podem impedir a execução dos
    testes de unidade a qualquer momento e em
    qualquer ambiente, como:
    ●
        File system
    ●
        Database connections
    ●
        Socket connections
    ●
        HTTP requests
Test Doubles
●
    Dummy: são objetos que nunca são utilizados,
    são criados apenas para completar a lista de
    parâmetros exigidos
●
    Fake: objetos que implementados APENAS para
    testes, e que NÃO DEVEM ser utilizados em
    produção (ex: conexão com DB em memória)
●
    Stubs: objetos que retornam dados falsos
●
    Mocks: parecido com stubs, porém além de
    retornar verifica número de chamadas e
    parâmetros utilizados
test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO stub)
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'src/Lcobucci/Utils/FileLog.php';
require_once 'PHPUnit/Framework/TestCase.php';
use LcobucciExamplesTestsCalculator;
class CalculatorTest extends PHPUnit_Framework_TestCase
{
    public function testLoggerCanBeAttached()
    {
        $logger = $this->getMock(
            'LcobucciUtilsFileLog',
            array('log')
        );
        $logger->expects($this->any())
               ->method('log')
               ->will($this->returnValue(true));
        $calculator = new Calculator($logger);
        $this->assertEquals(2, $calculator->div(4, 2));
    }
}
test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO mock)
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'src/Lcobucci/Utils/FileLog.php';
require_once 'PHPUnit/Framework/TestCase.php';

use LcobucciExamplesTestsCalculator;
class CalculatorTest extends PHPUnit_Framework_TestCase
{
    public function testMathOperationsMustBeLogged()
    {
        $logger = $this->getMock(
            'LcobucciUtilsFileLog',
            array('log')
        );

        $logger->expects($this->one())
               ->method('log')
               ->with('div', array(4, 2), 2)
               ->will($this->returnValue(true));

        $calculator = new Calculator($logger);
        $this->assertEquals(2, $calculator->div(4, 2));
    }
}
Outras funcionalidades
●
    Dependência entre métodos de teste
●
    Acessar atributos privados
●
    Acessar métodos privados
test/Lcobucci/Examples/Tests/StackTest.php
<?php
require_once 'PHPUnit/Framework/TestCase.php';

class StackTest extends PHPUnit_Framework_TestCase
{
    public function testStackIsInitiallyEmpty()
    {
        $stack = array();
        $this->assertEmpty($stack);

        return $stack;
    }

    /**
      * @depends testStackIsInitiallyEmpty
      */
    public function testAddingAnElementWorks(array $stack)
    {
         array_push($stack, 'test');
         $this->assertEquals('test', $stack[count($stack) - 1]);
    }
}
test/Lcobucci/Examples/Tests/PrivateAttributeTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_once 'PHPUnit/Framework/TestCase.php';

class PrivateAttributeTest extends PHPUnit_Framework_TestCase
{
    public function testPrivateAttribute()
    {
        $this->assertEquals(
            'teste',
            $this->readAttribute(
                new MyClass(),
                'myPrivateAttribute'
            )
        );
    }
}
test/Lcobucci/Examples/Tests/PrivateAttributeTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_once 'PHPUnit/Framework/TestCase.php';

class PrivateAttributeTest extends PHPUnit_Framework_TestCase
{
    public function testPrivateAttribute()
    {
        $this->assertAttributeEquals(
            'teste',
            'myPrivateAttribute',
             new MyClass()
        );
    }
}
test/Lcobucci/Examples/Tests/PrivateMethodTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_once 'PHPUnit/Framework/TestCase.php';

class PrivateMethodTest extends PHPUnit_Framework_TestCase
{
    public function testPrivateMethod()
    {
        $obj = new MyClass();
        $method = new ReflectionMethod(
            $obj, 'myPrivateMethod'
        );
        $method->setAccessible(true);

        $this->assertEquals(
            2,
            $method->invoke($obj, 4, 2)
        );
    }
}
Facilitadores dos testes de
unidade
●
    Baixo acoplamento
●
    Alta coesão
●
    Injeção de dependências
●
    Métodos pequenos
Complicadores dos testes de
unidade
●
    Escopo global
●
    Atributos e métodos estáticos
●
    Singleton
●
    Tarefas no construtor da classe
Test Driven Development
 “Test Driven Development (TDD) ou em português
 Desenvolvimento dirigido por testes é uma técnica de
 desenvolvimento de software que baseia em um ciclo
 curto de repetições: Primeiramente o desenvolvedor
 escreve um caso de teste automatizado que define uma
 melhoria desejada ou uma nova funcionalidade. Então, é
 produzido código que possa ser validado pelo teste
 para posteriormente o código ser refatorado para um
 código sob padrões aceitáveis.”

 http://pt.wikipedia.org/wiki/Test_Driven_Development
Só isso??
Benefícios do TDD
●
    Diminui ou até elimina a otimização prematura
●
    Código simples, eficaz e eficiente
●
    Cenários de testes bem definidos
Hora do exemplo!
E pra enfrentar bugs, como fica?
TDD na correção de bugs
1. Escreva um cenário de teste que reproduza o
  erro
2. Resolva o problema no código fonte (não
  pensa em deixar bonito, resolva o problema)
3. Execute os testes para garantir que nada foi
  comprometido
4. Refatore o código corrigido (agora sim pra
  deixar bonito)
5. Execute os testes novamente e seja feliz =D
Dúvidas???
Obrigado!

Eu por aí: http://about.me/lcobucci
Slides: http://slideshare.net/lcobucci


Avalie essa palestra: http://joind.in/3938

Testes de unidade e TDD SoLiSC 2011

  • 1.
    Testes de unidade avançados e TDD Luís Otávio Cobucci Oblonczyk 21 de Outubro de 2011 6° SoLiSC
  • 2.
    Luís Otávio CobucciOblonczyk ● Desenvolvedor PHP na Softnex Tecnologia ● Orientador no Senac TI ● Doido por PHP desde 2003 ● Perfeccionista ao extremo =P @lcobucci http://about.me/lcobucci
  • 3.
  • 4.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser
  • 5.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro
  • 6.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código
  • 7.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código 4) F5
  • 8.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código 4) F5 5) Muda o código
  • 9.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código 4) F5 5) Muda o código 6) F5
  • 10.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código 4) F5 5) Muda o código 6) F5 …
  • 11.
    Normalmente quando desenvolvemosfazemos o teste F5 1) Abre sistema no browser 2) Procura o erro 3) Muda o código 4) F5 5) Muda o código 6) F5 … 999) Resolve o problema
  • 12.
    Quanto tempo PERDEMOSnesta brincadeira?
  • 13.
    Quanto dinheiro PERDEMOSnesta brincadeira?
  • 15.
    E a perguntaque não quer calar...
  • 17.
    Quando isso acontecenum ambiente “separado” não tem GRANDES problemas
  • 18.
    E quando acontecena “vida real”?
  • 19.
    Tá, e oque eu posso fazer?
  • 20.
    Tá, e oque eu posso fazer? Automatizar alguns testes! (pra começar)
  • 21.
    Testes de Unidade “Teste de unidade é toda a aplicação de teste nas assinaturas de entradas e saídas de um sistema, consiste em validar dados válidos e inválidos via I/O (entrada/saída) sendo aplicado por desenvolvedores ou analistas de teste. Uma unidade é a menor parte testável de um programa de computador.” http://pt.wikipedia.org/wiki/Teste_de_unidade
  • 22.
    Testes de Unidade ● Te dá uma visão rápida do teu código: ou funciona ou não funciona ● Facilita o desenvolvimento, pois caso alguma alteração estrague uma funcionalidade já existente, você fica sabendo antes mesmo de ser enviado para os sistemas de controle de versão ● Servem também como parte da documentação ● Ajuda a manter a sanidade mental da equipe
  • 23.
  • 24.
    Utilizarei aqui funcionalidades presentesa partir do PHP 5.3.2! Se você ainda não usa o PHP 5.3 é uma boa oportunidade de começar ; )
  • 25.
    PHPUnit O queé: Framework p/ criação e execução de testes de unidade. Faz parte da família xUnit. Criador: Sebastian Bergmann Instalação: pear config-set auto_discover 1 pear install pear.phpunit.de/PHPUnit http://www.phpunit.de
  • 26.
  • 27.
    src/Lcobucci/Examples/Tests/Calculator.php <?php namespace LcobucciExamplesTests; class Calculator { public function sum($number1, $number2) { return $number1 + $number2; } }
  • 28.
    test/Lcobucci/Examples/Tests/CalculatorTest.php <?php require_once 'src/Lcobucci/Examples/Tests/Calculator.php'; require_once 'PHPUnit/Framework/TestCase.php'; useLcobucciExamplesTestsCalculator; class CalculatorTest extends PHPUnit_Framework_TestCase { public function testSum() { $calculator = new Calculator(); $this->assertEquals(4, $calculator->sum(2, 2)); } }
  • 29.
    TestCase e TestSuite ● O TestCase possui métodos de testes para um recurso (normalmente uma classe) ● TestSuites formam grupos de testes, ou seja dentro de um TestSuite podem ter vários TestCases ou até vários TestSuites
  • 30.
    tearDown() e setUp() ● São métodos protegidos (com visibilidade protected) disponíveis tanto no TestCase quanto no TestSuite ● Podem ser sobrecarregados caso NECESSÁRIO, e são utilizados para preparar e limpar o ambiente de teste ● No TestCase o setUp() é executado ANTES de cada método de teste e o tearDown ao FINAL de cada método
  • 31.
    tearDown() e setUp() ● No TestSuite o setUp() é executado ANTES de cada item da suite de testes e o tearDown ao FINAL de cada item
  • 32.
    Boas práticas ● Não crie métodos de teste que não teste absolutamente nada ● Não queira testar várias situações em um método só ● Use os métodos corretos para comparações ● $this->assertEmpty($var); vs $this->assertTrue(empty($var)); ● $this->assertInstanceOf('Exception', $e); vs $this->assertTrue($e instanceof Exception);
  • 33.
    Boas práticas ● Crie um método de teste para cada condição de teste de um método usando nomes significativos ● Mantenha os diretórios de teste similares aos diretórios das classes ● Use arquivo de configuração com um arquivo de bootstrap
  • 34.
    src/Lcobucci/Examples/Tests/Calculator.php <?php namespace LcobucciExamplesTests; use InvalidArgumentException; classCalculator { public function div($dividend, $divisor) { if ($divisor == 0) { throw new InvalidArgumentException( 'The divisor cannot be ZERO' ); } return $dividend / $divisor; } }
  • 35.
    test/Lcobucci/Examples/Tests/CalculatorTest.php <?php require_once 'src/Lcobucci/Examples/Tests/Calculator.php'; require_once 'PHPUnit/Framework/TestCase.php'; useLcobucciExamplesTestsCalculator; class CalculatorTest extends PHPUnit_Framework_TestCase { /** * @expectedException InvalidArgumentException */ public function testDivisorCannotBeZero() { $calculator = new Calculator(); $calculator->div(2, 0); } public function testDivMustReturnTheRightDivision() { $calculator = new Calculator(); $this->assertEquals(2, $calculator->div(4, 2)); } }
  • 36.
    Test Doubles ● São objetos “falsos” que utilizamos para isolar elementos que podem impedir a execução dos testes de unidade a qualquer momento e em qualquer ambiente, como: ● File system ● Database connections ● Socket connections ● HTTP requests
  • 37.
    Test Doubles ● Dummy: são objetos que nunca são utilizados, são criados apenas para completar a lista de parâmetros exigidos ● Fake: objetos que implementados APENAS para testes, e que NÃO DEVEM ser utilizados em produção (ex: conexão com DB em memória) ● Stubs: objetos que retornam dados falsos ● Mocks: parecido com stubs, porém além de retornar verifica número de chamadas e parâmetros utilizados
  • 38.
    test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO stub) <?php require_once'src/Lcobucci/Examples/Tests/Calculator.php'; require_once 'src/Lcobucci/Utils/FileLog.php'; require_once 'PHPUnit/Framework/TestCase.php'; use LcobucciExamplesTestsCalculator; class CalculatorTest extends PHPUnit_Framework_TestCase { public function testLoggerCanBeAttached() { $logger = $this->getMock( 'LcobucciUtilsFileLog', array('log') ); $logger->expects($this->any()) ->method('log') ->will($this->returnValue(true)); $calculator = new Calculator($logger); $this->assertEquals(2, $calculator->div(4, 2)); } }
  • 39.
    test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO mock) <?php require_once'src/Lcobucci/Examples/Tests/Calculator.php'; require_once 'src/Lcobucci/Utils/FileLog.php'; require_once 'PHPUnit/Framework/TestCase.php'; use LcobucciExamplesTestsCalculator; class CalculatorTest extends PHPUnit_Framework_TestCase { public function testMathOperationsMustBeLogged() { $logger = $this->getMock( 'LcobucciUtilsFileLog', array('log') ); $logger->expects($this->one()) ->method('log') ->with('div', array(4, 2), 2) ->will($this->returnValue(true)); $calculator = new Calculator($logger); $this->assertEquals(2, $calculator->div(4, 2)); } }
  • 40.
    Outras funcionalidades ● Dependência entre métodos de teste ● Acessar atributos privados ● Acessar métodos privados
  • 41.
    test/Lcobucci/Examples/Tests/StackTest.php <?php require_once 'PHPUnit/Framework/TestCase.php'; class StackTestextends PHPUnit_Framework_TestCase { public function testStackIsInitiallyEmpty() { $stack = array(); $this->assertEmpty($stack); return $stack; } /** * @depends testStackIsInitiallyEmpty */ public function testAddingAnElementWorks(array $stack) { array_push($stack, 'test'); $this->assertEquals('test', $stack[count($stack) - 1]); } }
  • 42.
    test/Lcobucci/Examples/Tests/PrivateAttributeTest.php Não é umaboa prática, mas de vez em quando é necessário... <?php require_once 'PHPUnit/Framework/TestCase.php'; class PrivateAttributeTest extends PHPUnit_Framework_TestCase { public function testPrivateAttribute() { $this->assertEquals( 'teste', $this->readAttribute( new MyClass(), 'myPrivateAttribute' ) ); } }
  • 43.
    test/Lcobucci/Examples/Tests/PrivateAttributeTest.php Não é umaboa prática, mas de vez em quando é necessário... <?php require_once 'PHPUnit/Framework/TestCase.php'; class PrivateAttributeTest extends PHPUnit_Framework_TestCase { public function testPrivateAttribute() { $this->assertAttributeEquals( 'teste', 'myPrivateAttribute', new MyClass() ); } }
  • 44.
    test/Lcobucci/Examples/Tests/PrivateMethodTest.php Não é umaboa prática, mas de vez em quando é necessário... <?php require_once 'PHPUnit/Framework/TestCase.php'; class PrivateMethodTest extends PHPUnit_Framework_TestCase { public function testPrivateMethod() { $obj = new MyClass(); $method = new ReflectionMethod( $obj, 'myPrivateMethod' ); $method->setAccessible(true); $this->assertEquals( 2, $method->invoke($obj, 4, 2) ); } }
  • 45.
    Facilitadores dos testesde unidade ● Baixo acoplamento ● Alta coesão ● Injeção de dependências ● Métodos pequenos
  • 46.
    Complicadores dos testesde unidade ● Escopo global ● Atributos e métodos estáticos ● Singleton ● Tarefas no construtor da classe
  • 48.
    Test Driven Development “Test Driven Development (TDD) ou em português Desenvolvimento dirigido por testes é uma técnica de desenvolvimento de software que baseia em um ciclo curto de repetições: Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade. Então, é produzido código que possa ser validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis.” http://pt.wikipedia.org/wiki/Test_Driven_Development
  • 49.
  • 50.
    Benefícios do TDD ● Diminui ou até elimina a otimização prematura ● Código simples, eficaz e eficiente ● Cenários de testes bem definidos
  • 51.
  • 52.
    E pra enfrentarbugs, como fica?
  • 53.
    TDD na correçãode bugs 1. Escreva um cenário de teste que reproduza o erro 2. Resolva o problema no código fonte (não pensa em deixar bonito, resolva o problema) 3. Execute os testes para garantir que nada foi comprometido 4. Refatore o código corrigido (agora sim pra deixar bonito) 5. Execute os testes novamente e seja feliz =D
  • 54.
  • 55.
    Obrigado! Eu por aí:http://about.me/lcobucci Slides: http://slideshare.net/lcobucci Avalie essa palestra: http://joind.in/3938