Aprendendo TDD! Testes guiando o desenvolvimento!

TDD é uma técnica que requer prática, como já falei nos outros posts da série Aprendendo TDD! Então precisamos praticar! Nesse post vamos pegar um exemplo simple, o Fizz Buzz e desenvolvê-lo utilizando testes como nosso guia e seguindo o fluxo do TDD.

Teste -> Código -> Refatoração

Antes de entrar no exemplo em si, vamos analisar o ciclo do TDD e entender qual a importância de cada fase. Você provavelmente já viu esse ciclo antes em palestras ou apresentações, mas é importante relembrar!

tdd-ciclo-vermelho-verde-refatora

Ciclo do TDD

A primeira etapa é escrever o teste que expressa o que você gostaria que o código fizesse. Como falado no post anterior, evite pensar em como você irá fazer o código e sim em como você gostaria de utilizá-lo.

Nesse ponto os testes obviamente vão falhar, pois não temos o código ainda, mas isso é bom. Se você escreve um teste e ele passa, provavelmente o teste não está explorando o cenário que você quer ou ele ele vai adicionar pouco ou nenhum valor.

Em seguida vamos escrever o código mínimo necessário para que o teste passe. A ideia aqui não é pensar na solução perfeita, mas sim na mais simples. Melhorias no código serão feitas no próximo passo do ciclo, então queremos chegar lá o quanto antes.

Na vida real é fácil perceber que o código mínimo vai precisar ser alterado já no próximo teste, mas ainda assim é importante manter o pensamento na solução mais simples possível.

Por fim vamos pensar em como refatorar o código, dado que temos testes que garantem que não vamos quebrar nada. A cada ciclo do TDD vamos ganhando mais entendimento sobre o problema e adaptando o código de maneira segura.

É importante lembrar não só de refatorar o código mas também os testes. No código buscamos evitar duplicação para facilitar a manutenção, nos testes buscamos melhorar a legibilidade para que pessoas possam entender melhor como nosso código funciona.

Utilizando o ciclo TDD

O Fizz Buzz é um jogo onde precisamos dizer os números mas com uma pequena mudança. Sempre que o número for divisível por 3 dizemos “fizz” ao invés do número, se for divisível por 5 dizemos “buzz” e se for divisível tanto por 3 quanto por 5 dizemos “fizz buzz”.

Então vamos fazer um programa que recebe um número e vai mostrar o que uma pessoa diria no Fizz Buzz até esse número. Para começar vamos fazer o caso mais simples, quando precisamos dizer apenas o número.

Crie uma nova pasta para o código e dentro dela adicione uma pasta lib e outra test. Dentro de test adicione o arquivo fizz_buzz_test.rb e vamos escrever nele nosso primeiro caso de teste:

# test/fizz_buzz_test.rb
require 'minitest/autorun'
require 'minitest/spec'
require_relative '../lib/fizz_buzz.rb'
describe FizzBuzz do
  it "retorna o numero quando nao for divisivel por 3 ou por 5" do
    fizz_buzz = FizzBuzz.new(1)
    contagem = fizz_buzz.gerar_contagem()
    contagem[0].must_equal '1'
  end
end

Lembrando do post anterior, você consegue identificar as decisões de design tomadas nesse código? Pense um pouco antes de continuar.

Ao criarmos uma nova classe FizzBuzz passamos para ela, já no construtor, até qual número queremos contar. Eu tomei essa decisões pois quero que um objeto FizzBuzz seja utilizado apenas uma vez para cada contagem, assim espero diminuir a complexidade dele.

Em seguida temos um método de um objeto FizzBuzz que vai gerar a contagem, retorna um conjunto com as strings relativas a cada número. Essa decisão foi tomada pensando para dar mais liberdade a quem utiliza o nosso FizzBuzz. Retornar um conjunto ao invés de, por exemplo, uma string com a contagem, permite que quem utilize o código manipule a contagem de maneira mais fácil.

Por fim, como retornamos um conjunto, o teste valida que na posição inicial vamos ter a string “1”. A decisão de retornar o conjunto também facilitou a nossa vida para escrever testes, já que não precisamos manipular uma string para verificar que o código funciona como esperado.

Execute o teste acima através do comando ruby test/fizz_buzz_test.rb. O teste deve falhar pois não temos nem a classe FizzBuzz, então se o seu teste falhar por esse motivo estamos no caminho certo 🙂

Agora podemos escrever o código para fazer esse teste passar. Precisamos criar a classe, o construtor e o método que vai gerar a contagem.

class FizzBuzz
  attr_reader :limite_contagem

  def initialize(limite_contagem)
    @limite_contagem = limite_contagem
  end

  def gerar_contagem()
    ['1']
  end
end

Lembre que precisamos escrever o código mais simples possível para chegar logo na fase de refatoração, onde todos os testes estão passando. Então basta que o método gerar_contagem retorne um array contendo a string esperada 🙂

Na vida real não vamos escrever código assim pois sabemos que ele vai precisar mudar logo, mas é importante manter na cabeça a simplicidade da solução. Por hora não temos a necessidade de refatorar nenhum código, então vamos para os próximos casos te teste.

Próximos passos

Qual seria o próximo caso de teste? Vale a pena testar uma contagem até o número 2?

Eu acho que não pois contar até o número 2 não vai expor nenhuma regra diferente. Podemos partir para testar a contagem até o número 3, pois vamos aplicar a regra do “fizz”.

# test/fizz_buzz_test.rb
it "retorna fizz quando o número for 3" do
  fizz_buzz = FizzBuzz.new(3)
  contagem = fizz_buzz.gerar_contagem()

  contagem[2].must_equal 'fizz'
end

Agora podemos implementar o código. Lembrando da ideia de manter a solução simples, podemos fazer simplesmente isso:

def gerar_contagem()
  ['1', '2', 'fizz']
end

Execute os testes novamente e eles devem passar! Agora que temos essa garantia podemos pensar em como escrever esse código melhor, já que criar um array na mão não é solução que vai escalar muito bem.

Podemos refatorar o código para gerar cada número e verificar se o número é divisível por 3 para retornar “fizz”.

def gerar_contagem()
(1..@limite_contagem).map { |numero|
numero % 3 == 0 ? 'fizz' : numero.to_s
}
end

Execute os testes novamente e eles devem continuar passando! É importante lembrar que, enquanto refatoramos o código os testes devem passar a cada mudança. Se você passa muito tempo com os testes falhando é um sintoma de que ou a mudança é muito grande, e deveria ser dividida em passos menores, ou você não entende muito bem o código que precisa ser modificado.

Mais TDD!

Ainda temos duas regras que não foram documentadas nos testes: quando o número for divisível por 5 e quando for tanto por 3 quanto por 5. Você consegue pensar em como quais seriam os próximos testes?

Uma vez que as regras de negócio estão documentadas nos testes, podemos pensar em outros casos que não estão cobertos. Quais casos, além dos dois já falados poderíamos adicionar? O que acontece quando passarmos um número negativo para gerar a contagem? E se nenhum número for passado?

Outro exercício interessante é modificar o design da aplicação e recomeçar o ciclo. Por exemplo, imagine agora que não queremos criar um objeto para cada sequência FizzBuzz. Ao invés disso vamos ter apenas um método de classe que vai gerar a contagem e retornar o mesmo array.

Espero que tenha gostado desse post e que tenha conseguido desenvolver o código utilizando os testes como guia. Tente terminar o exercício e pensar em maneiras mais simples de resolvê-lo, lembrando sempre de deixar os testes passando!

Continue acompanhando a série com posts semanais Aprendendo TDD! Compartilhe com amigos e colegas que querem aprender. Se tem alguma sugestão, dúvida ou comentário use o espaço aqui do blog ou me procure no twitter em @marcosbrizeno. Até a próxima!

 

Anúncios

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