Posts made in July, 2008

Test Driven Development

Posted by on Jul 12, 2008 in Agile, Software | 3 comments

O contato com Scrum, XP e Agile em geral, me levaram à diversos conceitos que até então desconhecia, um dos quais resolvi me aprofundar  foi TDD (Test Driven Devolpment), para isso li o livro ”Test-Driven Development by Example” escrito por Kent Beck. O Livro é muito esclarecedor e divertido de ler, apresenta diversos conceitos e técnicas através de exemplos de implementações de pequenas soluções.
TDD ou Desenvolvimento Dirigido por Testes, é uma técnica de desenvolvimento de software que consiste em pequenas iterações onde testes são escritos primeiro e o código  produzido é somente o necessário para fazer o teste passar, e finalmente o código é refatorado para acomodar as mudanças. TDD proporciona feedback rápido depois de cada mudança. Não é considerado somente  uma ténica de escrita de testes, mas uma técnica para design de software.

chemistry por tschoppi

chemistry por tschoppi

TDD pode ser facilmente explicado em cinco simples passos:

  1. Adicione um teste rapidamente.
  2. Execute todos os testes e observe o novo teste falhar.
  3. Faça uma pequena mudança para fazer o teste passar .
  4. Execute todos os teste e observe que foram bem sucedidos.
  5. Refatore e remova o código duplicado.

Para melhor entender este ciclo de cinco fases, vamos constuir um pequeno programa Java para realizar um simples cálculo de potência. Para executar os testes precisamos de uma ferramenta de testes unitários, utilizaremos o JUnit.

O JUnit possui uma classes chamada TestCase, a qual nossa classe de teste deverá extender. Essa classe possui uma série de métodos que nos auxiliarão no processo de testes unitários, tais métodos fazem o teste falhar caso determinadas condições não sejam satisfeitas.

Ex: O método “AssertEquals(x,y)“  faz o teste falhar caso x seja direferente de y.

  assertEquals(“TDD”,”DDD”); //Falha
  assertEquals(100, 100); //Passa

Começaremos então pelo teste:

  public class CalculadoraTest extends TestCase {
    public void testCalcularOitoAoQuadrado(){
      Calculadora calculadora = new Calculadora();
      assertEquals(calculadora.calcularPotencia(8, 2), new BigDecimal(64));
    }
  }

Se tentarmos executar nosso teste, o JUnit nos apresentará uma barra vemelha o que signfica que o teste falhou, e falhou porque a classe Calculadora e o método estático calcularPotencia ainda não existem. Vamos então criar a classe e o método para fazer o teste ao menos compilar.

  public class Calculadora {
    public void calcularPotencia(Integer a, Integer b){
      return null;
    }
  }

Nossa missão agora é fazer, rapidamente, o teste passar. Para isso vamos então retornar o valor que nosso primeiro teste espera, 64.

  public class Calculadora {
    public void calcularPotencia(Integer a, Integer b){
      return new BigDecimal(64);
    }
  }

Se executarmos o teste novamente o mesmo passará… Eu seu, eu sei, que isso pode parecer meio estranho no inicio, mas não se assuste, você se acostumará e entenderá melhor o que está por traz disso tudo com o tempo. Vamos adicionar mais um método de teste em nossas classe de testes

    public void testCalcularDoisAoCubo(){
      Calculadora calculadora = new Calculadora();
      assertEquals(calculadora.calcularPotencia(2, 3), new BigDecimal(8));
    }

E vamos fazer a alteração mais simples possível para fazer o teste passar:

  public class Calculadora {
    public void calcularPotencia(Integer a, Integer b){
      if (b == 2)
        return new BigDecimal(64);
      else
        return new BigDecimal("8");
    }
  }

Agora que os dois testes estam passando, realizaremos o processo de refatoração para remover as constantes e a redundancia do código, assegurando que o teste continue passando.

  public void calcularPotencia(Integer a, Integer b){
    BigDecimal result = new BigDecimal(a);
    for (int I = 0; I < b; I++) {
      result = result.multipliedBy(BigDecimal.valueOf(a));
    }
    return result;
  }

Pronto! Refatoramos nosso código removendo as constantes e se executarmos nossos testes verificaremos que este será bem sucedido! Este é o processo básico de TDD.

Ops!!! O que acontecerá se tentarmos executar o método executar uma potencia de um número elevado a zero? Problemas!!! Todo o número elevado a Zero é igual a 1 (Um), dessa forma devemos adicionar um novo teste que verifique se um número elevado a zero retorna 1, fazer o teste passar, refatorar e assegurar de que os outros testes continuem passando.

  public void calcularPotencia(Integer a, Integer b){
    if (b.equals(0)){
      return BigDecimal.valueOf(1);
    }
    BigDecimal result = new BigDecimal(a);
    for (int I = 0; I < b; I++) {
      result = result.multipliedBy(BigDecimal.valueOf(a));
    }
    return result;
  }
  public void testTodoNumeroElevadoAZeroDeveRetornarUm(){
    Calculadora calculadora = new Calculadora();
    assertEquals(calculadora.calcularPortencia(21, 0), new BigDecimal(1));
    assertEquals(calculadora.calcularPortencia(8, 0), new BigDecimal(1));
  }

É isso pessoal! Parece simples, mas não é fácil. Depois muito treinamento e prática em diferentes cenários isso começa a ficar mais natural e você passa a enxergar mais claramente os benefício desta poderosa técnica. Estou a disposição para esclarecimento de dúvidas. Comentários são sempre muito bem vindos.

Read More

Sobre Generics e Raw Types em Java

Posted by on Jul 3, 2008 in Software | 1 comment

O Java 5 introduziu o conceito de Generics, o que tem facilitado muito a manipulação de collections.

Quem tabalhou na migração de projetos do Java 1.4 para Java 5 percebeu o grande número de warnings que passam a ser exibidos sobre raw types. São os unchecked warnings.

Mas então, que diabo é um raw type?
Raw type é o tipo “cru” do elemento, ou seja sem generics. Exemplo:

 
List lista = new ArrayList(); //Raw Type
List nomes = new ArrayList(); //Generic Type

Qual é o problema de se utilizar raw types?
O problema é que raw types não tem a clareza e segurança que os generics tem.

Como assim?
Ao usar tipos generics você percebe um possível problema o mais rápido possível, ou seja, em tempo de desenvolvimento. Classcast exceptions deixam de ser um tormento para você.

Por exemplo:

Com raw type

List numeros = new ArrayList();
numeros.add(30);
Integer n = numeros.get(0);
//Classcast exception em tempo de execução.

Com generic type

List<Integer> numeros = new ArrayList<Integer>();
numeros.add(30);
// Não compila :.)

Mas se eu nunca devo usar esses raw types porque eles existem?
Os raw types existem por uma questão de compatibilidade do Java, códigos que foram escritos em versões anteriores precisam continuar funcionando, por isso o compilador exibe apenas warnings.

Ok. Mas o que devo fazer em casos em que não sei exatamente que tipo devo tratar em uma collection?
Para casos assim existem os “unbounded wildcard types”, representa-se com um ponto de interogação. Veja:

boolean temOMesmoNumeroDeElementos(Set&lt;?&gt; a, Set&lt;?&gt; b){
   return a.size() == b.size();
}

O uso de wild cards torna seu método seguro porque impede que qualquer valor diferente de null seja inserido em sua collection.

Raw types geram warnings conhecidos como unchecked warnings. Elimine todos que você puder! Todo unchecked warning representa um posssível ClassCastException em Runtime. Se não for possível eliminar o warning, utilize a annotation @SupressWarnings(“unchecked”) sempre no menor escopo possível (variável, bloco, método, etc.).

Concluindo, não use raw types em código novo, entenda os beneficios que se tem ao utilizar generics e refatore o código antigo tornando-o mais seguro e claro.

Read More