Aprendendo TDD! Mais sobre dublês de teste

No post anterior vimos como os dublês de testes, mais conhecidos por mocks, podem nos ajudar a testar alguns cenários “impossíveis”. Vamos então entender como deixar nossos testes mais confiáveis, isolados e rápidos com outros tipos de dublês!

O artigo The Little Mocker, de Uncle Bob, é leitura recomendadíssima para aprender mais sobre os diferentes tipos de dublês.

Como exemplo vamos imaginar que queremos escrever testes que dependem da seguinte classe que é responsável por autorizar o pagamento de um cartão de crédito:

class ServicoValidacaoPagamento
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
    # Lógica complexa para autorizar um pagamento
  end
end

Stub

Utilizamos um Stub quando não precisamos nos preocupar com a execução de um método, apenas com o resultado que ele retorna. Para criar um stub para o código anterior, poderíamos fazer algo como:

class ServicoValidacaoPagamentoStub
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
    true
  end
end

Sempre que utilizarmos ServicoValidacaoPagamentoStub todos os pagamentos serão autorizados, assim para os nossos testes não precisamos nos preocupar com a sua lógica para retornar o que queremos.

Para testar que um pedido é finalizado quando o pagamento é validado poderíamos utilizar o stub assim:

it "marca pedido como finalizado quando pagamento eh aprovado" do
  pedido = Pedido.new(ServicoValidacaoPagamentoStub.new)
  pedido.adicionar_produtos(produtos)
  pedido.forma_de_pagamento(cartao_de_credito)

  pedido.finalizar # vai chamar o serviço para validar o pagamento

  pedido.estado.must_equal EstadoPedidos::FINALIZADO
end

Spy

Um Spy é utilizado quando queremos validar que métodos foram chamados, quantas vezes, com quais argumentos e quaisquer outras informações que nos interesse.

Supondo que agora queremos validar que, ao finalizar o pedido, estamos utilizando os dados corretos do cartão de crédito para validar o pagamento. Esse é um spy que poderíamos utilizar:

class ServicoValidacaoPagamentoSpy
  attr_reader :numero_cartao, :mes_expiracao, :ano_expiracao, :autorizacao_executada
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
    @autorizaca_executada = true
    @numero_cartao = numero_cartao
    @mes_expiracao = mes_expiracao
    @ano_expiracao = ano_expiracao
    true
  end
end

Com o Spy, além de forçar o valor de retorno também estamos guardando informações se o método foi chamado e quais argumentos foram passados. O teste utilizaria o Spy da seguinte forma:

it "chama servico de validacao ao finalizar pagamento" do
  spy = ServicoValidacaoPagamentoSpy.new
  pedido = Pedido.new(spy)
  pedido.adicionar_produtos(produtos)
  pedido.forma_de_pagamento(cartao_de_credito)

  pedido.finalizar # vai chamar o serviço para validar o pagamento

  spy.autorizaca_executada.must_equal true
spy.numero_cartao.must_equal cartao_de_credito.numero
spy.mes_expiracao.must_equal cartao_de_credito.mes_expiracao
spy.ano_expiracao.must_equal cartao_de_credito.ano_expiracao
end

Utilizando um spy quando queremos validar informações relacionadas a como o método foi executado.

Mock

Assim como o Spy, o Mock também valida métodos que foram chamados. A diferença é bem sútil, um Mock sabe o que deve ser testado, ao contrário do Spy que apenas guarda informação da execução.

class ServicoValidacaoPagamentoMock
  attr_reader :autorizacao_executada
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
    @autorizaca_executada = true
    true
  end

def verifica_execucao()
autorizacao_executada.true?
end
end 

Um mock possui a lógica de validação dentro dele, assim o teste fica mais simples. No entanto a lógica de verificação pode ser mais complexa do que apenas se o método foi executado, como mostra o exemplo. O teste ficaria assim:

it "chama servico de validacao ao finalizar pagamento" do
  mock = ServicoValidacaoPagamentoMock.new
  pedido = Pedido.new(mock)
  pedido.adicionar_produtos(produtos)
  pedido.forma_de_pagamento(cartao_de_credito)

  pedido.finalizar # vai chamar o serviço para validar o pagamento

  mock.verifica_execucao.must_equal true
end

Stub, Spy ou Mock?

A razão pelo qual Dublês de Testes são conhecidos como mocks é que a diferença entre eles é bem sutil e muitas vezes os frameworks vão empacotar todas essas funcionalidades e chamar de mock.

Na prática utilizamos na maioria das vezes stubs, para apenas substituir o comportamento de um método por seu valor fixo, ou um spy, para avaliar informação relacionadas a chamadas de método. Mas como os autores dos frameworks decidiram chamar tudo de mock ¯_(⌣̯̀⌣́)_/¯

Se quiser ir mais a fundo para entender a diferença entre eles, sugiro o artigo Mocks Aren’t Stubs de Martin Fowler que contém vários exemplos e mais detalhes.

Dummy

Um Dummy é utilizado para substituir um objeto quando você não precisa se importar como ele será utilizado. Geralmente ele é utilizado para melhorar a legibilidade, já que não possuem nenhum comportamento. Esse seria um Dummy para o serviço de autorização de pagamento:

class ServicoValidacaoPagamentoDummy
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
end
end

O Dummy recebe chamadas, mas não faz nada com elas. O principal objetivo é melhorar a legibilidade, já que sempre que criamos um pedido precisamos passar um serviço podemos utilizar o Dummy ao invés de um nil:

it "adicionar produtos ao pedido" do
  pedido = Pedido.new(ServicoValidacaoPagamentoDummy.new)
  pedido.adicionar_produtos(produtos)

pedido.quantidade_produtos.must_equal 1
end

Fake

Semelhante ao Dummy, o Fake é utilizado para substituir objetos no entanto ele possui uma determinada lógica que é será utilizada em diferentes cenários de teste. Por exemplo:

class ServicoValidacaoPagamentoFake
  def autorizar_pagamento(numero_cartao, mes_expiracao, ano_expiracao)
    numero_cartao == "1234-1234-1234-1234"
  end
end 

Assim, podemos utilizar nos testes e simular a falha/sucesso do pagamento sem precisar mudar o dublê.

it "marca pedido como finalizado quando pagamento eh aprovado" do
  pedido = Pedido.new(ServicoValidacaoPagamentoFake.new)
pedido.adicionar_produtos(produtos)
pedido.forma_de_pagamento(cartao_de_credito_valido)
# cartao_de_credito_valido possui o numero esperado pelo Fake

pedido.finalizar # vai chamar o serviço para validar o pagamento

pedido.estado.must_equal EstadoPedidos::FINALIZADO
end

it "marca pedido como pendente quando pagamento nao eh aprovado" do pedido = Pedido.new(ServicoValidacaoPagamentoFake.new)
pedido.adicionar_produtos(produtos)
pedido.forma_de_pagamento(cartao_de_credito_invalido)
# cartao_de_credito_invalido possui outro numero

pedido.finalizar # vai chamar o serviço para validar o pagamento

pedido.estado.must_equal EstadoPedidos::PENDENTE
end

Dublês de Teste na vida real

Na maioria das vezes que utilizei dublês foi através de uma biblioteca que cria esses objetos. Apesar da facilidade que a biblioteca proporciona é importante entender o motivo da utilização do dublê e também saber que escrever seu próprio não é algo tão difícil assim.

Espero que tenha gostado desse post e que consiga utilizar dublês de teste com mais efetividade! Apesar de ser uma excelente ferramenta eles precisam ser utilizados com cuidado e bastante consciência.

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

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!

 

Aprendendo TDD! O pensamento de Desenvolvimento Guiado por Testes

Uma das partes mais difíceis de fazer TDD é pensar em um código que não existe. Como você chama o método? Como vai fazer as asserções pra garantir que ele funciona?

Na verdade essa reflexão sobre o código que ainda está por vir é que é o maior benefício do TDD! Fazer essas perguntas sem ter tido todo o trabalho de escrever o código é uma grande ajuda.

O que pensamos quando estamos escrevendo o código

Ao escrever o código de produção eu me concentro muito mais no estilo da escrita, no tamanho que o método está ficando, se posso extrair alguma parte desse método etc.

Estamos pensando em fazer certo o código que estamos desenvolvendo. A visão fica mais estreita e muitas vezes coisas óbvias acabam passando totalmente desapercebido, como declarar uma variável chamada p, ou um if else para retornar true ou false.

Isso não é algo ruim, quando estamos escrevendo um código precisamos estar focados. O ponto é ter um balanço entre o foco em algo bem específico e a visão do todo, para garantir que não só estamos resolvendo o problema corretamente como estamos resolvendo o problema correto.

Alguns estudos expõe essa nossa capacidade muito bem. Esse vídeo é um excelente exemplo de exercício de foco: https://www.youtube.com/watch?v=IGQmdoK_ZfY.

O que pensamos quando estamos escrevendo os testes

Quando mudamos nossa atenção para escrever os testes devemos pensar em como o código, que ainda não existe, será utilizado.

Essa mudança de pensamento tem um efeito interessante. Tente responder o seguinte exercício que é bem conhecido nos estudos de psicologia cognitiva:

  • Um taco e uma bola de baseball custam juntos U$1.10.
  • O taco custa um dólar a mais que a bola.
  • Quanto custa a bola?

Se você pensou que a resposta é U$0.10, você errou, mas não está só. Eu também pensei que a resposta seria o mais óbvio, mas se a bola custasse U$0.10, então a soma seria U$1.20.

Escrever testes ajuda a evitar que esse pensamento rápido e focado em resolver o problema na nossa frente acabe por nos fazer errar. Se você tivesse um teste para essa situação, ao colocar o valor da bola como R$0.10, o teste falha e você recebe o feedback. Fabio Pereira explora muito mais esse assunto no capítulo “Psicologia cognitiva explicando Ágil” do livro ThoughtWorks Antologia Brasil.

Desenvolvendo Guiado por Testes

Vamos analisar agora as várias decisões de design que tomamos ao escrever os testes primeiro. Veja o exemplo de teste a seguir:

require 'minitest/autorun'
require 'minitest/spec'
require_relative '../lib/quadro.rb'

describe Quadro do
  it "realiza jogada na posicao especificada" do
    quadro = Quadro.new
    quadro.executar_jogada('x', 1, 1)
    quadro.posicoes[1][1].must_equal 'x'
  end
end

O objeto quadro é responsável por realizar as jogadas, mas precisa receber qual o símbolo será colocado e em qual posição nos eixos x e y. Após a jogada, a matriz que guarda as posições conterá o o símbolo na posição especificada, mas o método em si não retorna nada.

Apesar de ser um teste bem simples, todas essas decisões de design foram tomadas. Ao pensar no teste sem ter o código real escrito temos a oportunidade de visualizar como esse método será utilizado, ao invés de como ele vai funcionar.

Poderíamos, por exemplo, fazer com que o método retorne um booleano que indica se a jogada foi realizada ou não. Ou então, ao invés de passar o símbolo como parâmetro, poderíamos criar dois métodos, um que faz a jogada para o ‘x’ outro para ‘o’. Poderíamos criar um classe Jogador que armazenaria o símbolo.

O ponto principal é, ao escrever o teste antes pensamos em como vamos utilizar o código e não em como vamos fazê-lo. Então ao fazer TDD evite pensar em o que você faria o código e sim em como seria a melhor maneira de utilizá-lo!

Nos próximos posts vamos criar mais testes para nosso projeto de jogo da velha e usá-lo para refletir sobre o design do código. Fique ligado!

Compartilhe os posts da série Aprendendo TDD! com seus amigos e colegas que também estão buscando aprender mais sobre essa técnica. E acompanhe o blog pois toda semana tem post novo! Dúvidas, sugestões e comentário podem ser feitas aqui no blog ou no twitter em @marcosbrizeno.

[Refatorando tudo!] Não retorne nulo. Nunca.

Considere um exemplo bem simples: precisamos exibir a lista com as últimas compras que um usuário fez.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) { ... }

Para complicar um pouco mais, imagine que existe um serviço cuja responsabilidade é gerenciar compras. Esse serviço expõe uma interface que retorna o histórico de compras de um usuário, dado o seu email.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    return null;
  }
}

Esse código funciona bem, mas tem um grande problema (que pelo título do post você já deve ter percebido): ele nem sempre retorna o histórico de compras.

Não me obrigue a saber de coisas!

Como Rob Looby explica nesse post fantástico, existem milhares de coisas que nós desenvolvedores precisamos manter na cabeça: a estrutura do programa, o novo design que queremos seguir, boas práticas do time, regras de negócio etc. Os possíveis retornos de uma função/método não deveriam ser uma dessas coisas.

Para quem escreveu o código acima, retornar nulo pode parecer como uma boa decisão. Não tenho nenhum histórico de compras para exibir, então não retorno nada. Como se representa nada? Com um valor nulo.

Se você também acha que null é a melhor maneira de representar nada, sugiro assistir a apresentação da Sandi Metz, Nothing is Something (na verdade assista a palestra independente do que você acha, vale muito a pena :D).

Na grande maioria das vezes, quando retornamos nulo na verdade não queremos retornar um nulo, mas sim dizer algo mais. No exemplo acima queremos dizer que não encontramos nenhum histórico! O que é bem diferente de nulo.

O grande problema é que ao definir uma função que retorna um valor especial, seja ele nulo, 0, -1 ou qualquer outro número mágico, estamos assumindo que quem utilizar esse código vai saber e lembrar disso sempre que usar a nossa função. Por exemplo:

[1,2,3].index(2)
# => 1
[1,2,3].index(5)
# => nil

Em ruby, ao chamar o método index com um valor inexistente, nil é retornado. Dependendo do contexto, pode até fazer sentido já que nil é considerado falso em uma checagem de booleano. Mas de novo, obrigamos a pessoa que utiliza o método a lembrar disso, sempre!

Nada é alguma coisa

Ao invés de retornar um valor especial, podemos ser mais explícitos e diminuir a responsabilidade de quem vai usar nosso código. Considerando o exemplo inicial, vamos imaginar agora dois cenários:

  1. Dado o email de um usuário, quando nenhum histórico for encontrado, então uma mensagem de erro deve ser exibida na tela.
  2. Dado o email de um usuário, quando nenhum histórico for encontrado, então nenhuma informação deve ser exibida.

No primeiro cenário podemos melhorar o nosso código utilizando uma exceção:

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    throw new HistoricoNaoEncontrado("Histórico inexistenete para o email: " + email);
  }
}

Assim, a pessoa que utilizar o código vai saber que precisa tratar a exceção HistoricoNaoEncontrado e, seguindo o cenário, vai exibir a mensagem de error quando ela for lançada.

Já no segundo cenário, quando basta não exibir nenhum informação na tela, podemos utilizar o Padrão de Projeto Null Object. Um Null Object é simplesmente uma representação de um objeto que não possui nenhuma informação, mas que também não retorna nulo ou lança exceções.

Por exemplo, supondo que a classe HistoricoDeCompras possua uma lista de Compras, podemos criar um Null Object para ela que possua o mesmo tipo, através de herança, e que tenha uma lista de Compras válida, mas vazia:

public class HistoricoDeCompras {
  private List<Compras> compras;
  ...
}

public class HistoricoDeComprasNulo extends HistoricoDeCompras {
  private List<Compras> compras = new ArrayList();
  ...
}

Assim, ao retornar o HistoricoDeComprasNulo, não precisamos obrigar a pessoa a lembrar nenhum detalhe de implementação, o código continua funcional.

public HistoricoDeCompras buscaHistoricoDeCompras(String email) {
  RespostaHttp resposta = servicoDeCompras.obterHistorico(email);
  if (resposta.getStatus().sucesso() ) {
    return converterParaHistoricoDeCompras(resposta.getConteudo());
  } else {
    return new HistoricoDeComprasNulo();
  }
}

Em Scala, Java 8 e várias outras linguagens temos a nossa disposição valores opcionais. Com eles deixamos a cargo de quem utiliza o método lidar com a falta de informação, mas sem precisar retornar um valor mágico.

var helloWorld: Option[String] = Some("Hello World!")
println(helloWorld.getOrElse("Hello who?"))
// Hello World!
var helloWorld: Option[String] = None
println(helloWorld.getOrElse("Hello who?"))
// Hello who?

Valores opcionais permitem definir o que fazer quando o dado não está presente. No exemplo acima, em Scala, utilizamos Some para definir um valor válido e None para um valor inexistente. Ao utilizar o método getOrElse podemos definir o que será retornado caso nada seja encontrado.

Esse post do Rodrigo Turini explica com mais detalhes como utilizar valores opcionais no Java 8: http://blog.caelum.com.br/chega-de-nullpointerexception-trabalhe-com-o-java-util-optional/.

Pode parecer besteira ter essa trabalheira a mais só para não retornar null, mas acredite, o seu “eu do futuro” vai te agradecer se você parar de fazer isso!

Não se esqueça de conferir o links do post para se aprofundar mais no assunto.

Compartilhe o texto com seus colegas para evitar que o “eu do futuro” deles também sofra com esses problemas e continue acompanhando os posts da série [Refatorando tudo!] para melhorar ainda mais seu código!

Pair Programming

Existem vários artigos por aí falando sobre Pair Programming, muitos deles são bons e realmente ajudam a entender a prática e como iniciar a sua aplicação na sua equipe. Nesse post eu vou falar sobre a experiência que tive com pair programming, comentando os pontos positivos e algumas dificuldades e como elas foram superadas.

Para quem não sabe o que é Pair Programming

Acho que não existe nenhum desenvolvedor que não conheça a técnica, sério.

Alguns pontos importantes a serem notados é que a prática faz parte do XP[1], ou seja, é importante avaliar a prática junto com o resto do ecossistema da metodologia.

A programação em par, junto com as mudanças de pares são o centro da propriedade coletiva de código do XP. A propriedade coletiva do código é o centro da etapa de desenvolvimento do XP, que por sua vez é o centro das interações. Por tanto a programação em par pode ser considerada uma das bases do XP. Para mais informações visite o site oficial do XP [1] e veja o “flow chart” do XP.

Compartilhando o conhecimento

Trabalhar em uma parte do código lhe dá confiança para não só realizar as tarefas que foram atribuídas, mas também discutir sobre as questões que pairam ao redor daquela parte do sistema. Por tanto é muito importante que cada membro trabalhe com o maior parte possível do sistema, idealmente com todo o sistema.

Para que esse conhecimento seja compartilhado por toda a equipe a programação em par desempenha um papel chave. Se você desenvolve uma funcionalidade com alguém, quando outra pessoa vem parear com você ela ganha conhecimento nessa área também. Mais tarde, quando você vai parear com outra pessoa, você ganha conhecimento na outra funcionalidade. A rotação entre os pares também é essencial para esse processo.

Code Review ao extremo

Uma prática não tão comum entre equipes é a revisão de códigos. Uma pessoa que não esteve envolvido na produção do código  olha o código e sugere mudança e avalia os impactos das decisões tomadas na produção do código. A programação em par leva essa prática ao extremo pois enquanto uma pessoa produz o código outra faz a revisão na mesma hora.

O piloto, desenvolvedor que está programando ativamente, tem como preocupação fazer o teste passar o mais rápido possível (considerando que em pleno 2012, não existe desenvolvedor que não escreve testes). O navegador, desenvolvedor que está revisando o código, tem como objetivo analisar o código e avaliar o impacto das decisões considerando o código como um todo.

É importante trocar papéis durante uma seção de pareamento, pois é fácil para o piloto ficar cansado, bem como é fácil para o navegador se distrair com outras coisas. As vezes o entrosamento do par é tão grande que a mudança ocorre naturalmente, com um desenvolvedor peando o teclado para si.

No entanto pode ser importante chegar a um acordo sobre as mudanças, como por exemplo utilizar a técnica do ping-pong, onde uma pessoa escreve o teste, a outra escreve o código para fazer o teste passar e em seguida ambos sugerem refatorações.

Compartilhando a configuração

É importante notar que a programação em par envolve duas pessoas (dahh). Mais do que duas pessoas, dois desenvolvedores. Cada um tem suas opiniões, preferências e esquisitices. As diferenças vão desde o browser preferido até os atalhos personalizados que cada um utiliza. Assim o ambiente de trabalho precisa ser bem semelhante para que ambos os desenvolvedores sintam-se livres para trabalhar.

Uma solução é ter um computador neutro, com configurações que todos concordam, ou pelo menos aceitam utilizar. Desta forma, os desenvolvedores acabam se acostumando a trabalhar com estas configurações e os conflitos são evitados. Além disso cada um pode utilizar seu próprio laptop para resolver problemas pessoais, verificar e-mails etc.

Caso ambos precisem codificar em um computador pessoal, então é melhor que os pares cheguem a um acordo sobre as configurações ou com o tempo revesem o computador em uso para que cada um possa utilizar suas configurações.

Discutindo a relação

Outro ponto importante é sobre o código. Desenvolvedores costumam ter opiniões muito fortes sobre a “melhor maneira” de se resolver algum problema. Se os pares não souberem administrar as diferenças e aceitarem as opiniões uns dos outros, simplesmente não é possível que os pares consigam trabalhar juntos.

Uma boa ideia é juntar programadores experientes com outros menos experientes, assim um pode passar o conhecimento que possui para o outro. Mesmo quando dois desenvolvedores experientes estão pareando, é importante notar duas coisas: (1) o código possui um pouco do seu estilo de programar, ou seja, você consegue reconhecer o código como seu, e (2) o código não parece ser escrito por você por si só, ou seja, o código possui uma parte do estilo do outro desenvolvedor.

Apesar de parecerem opostos, ou até mesmo contrários, os dois pontos acima asseguram que ambos os pares tenham influencia no código produzido. Dessa forma ambos os desenvolvedores tem o sentimento de propriedade do código e podem assumir a responsabilidade pelo que foi feito.

Pair Programming != Mentoring

A programação em par, mesmo entre pares em diferentes estágios de experiência, não deve ser encarada como uma pessoa ensinando outra. Nestes casos, o maior problema é que o par com menos experiência acaba se inibindo e deixando de opinar sobre o código.

O desenvolvedor mais experiente deve deixar espaço para que o desenvolvedor menos experiente opine, pois assim ele tem a chance de refletir sobre o seu conhecimento do sistema. Também é importante que fique claro para ambos os motivos de uma ideia não ser aceita, ao invés de apenas dizer que a ideia não é boa, ou “o meu jeito é melhor”.

Para quem quiser explorar um pouco mais sobre Pair Programming eu recomendo muito o artigo da ImproveIT [2], é bem extenso mas vale muito a pena.

Referências:

[1] http://www.extremeprogramming.org/

[2] http://improveit.com.br/xp/praticas/programacao_par

O que aprendi assistindo The Avengers

Sério, esse é o filme mais fodástico de super heróis que já assisti (melhor até mesmo que X-Men First Class). Eu sai da sala do cinema impressionado, não pelos efeitos especiais, ou pelas cenas de lutas, ou simplesmente porque todos disseram que o filme era bom. O mais impressionante do filme são as lições que você aprende sobre trabalhar em equipe.

Sabe as vagas de emprego que você vê por aí em listas e sites, as vezes eu penso que existe um site que gera isso (hmm… ideia interessante), porque tem umas partes que são praticamente padronizadas. Uma delas é justamente “Trabalhar bem em equipe” ou “Gostar de trabalhar em equipe”.

Trabalhar em equipe é bem mais do que aquela frase clichê de que “um supri as dificuldades dos outros”. Juntar vários especialistas não quer dizer que a equipe é multidisciplinar. Apesar de ser meio batido, os valores propostos pelo XP definem bem o como trabalhar em equipe [1].

Os problemas com a Comunicação e Feedback são evidenciados quando Tony Stark fala que os segredos dos diretor Fury tem segredos. E isso quebra a Coragem e o Respeito da equipe, que acaba por entrar em colapso, levando o projeto a uma falha iminente. A melhor equipe do mundo, com os melhores recursos não consegue ter a Simplicidade para resolver a situação (tudo bem que essa é a parte que dá a emoção ao filme :D).

Eis que surge então a motivação para a equipe. Em um momento do filme o diretor Fury diz “But I believe, with the right push, they can be a team”. Ou seja, o grande lance de trabalhar em equipe não é (exatamente) o salário, o ambiente, o horário de trabalho, promoções, etc. Trabalhar em equipe é uma questão de motivação.

Se toda a equipe quer realmente resolver o problema, ele será resolvido mais cedo ou mais tarde. Agora se a equipe quer apenas ficar 8 horas no escritório para receber o salário no final do mês, acredite, nem mesmo dobrando o salário de todos o projeto terá um sucesso garantido. E essa questão da motivação é tão profunda que aconselho a leitura do post sobre Gestão Moderna no blog da Aspercom [3].

Outro ponto importante ao se trabalhar em equipe é a liderança. Quem é o líder do grupo? O diretor Fury está mais para um Gerente de Produto do que para um líder. Ele negocia com aquelas silhuetas no monitor, isolando a equipe e deixando que eles trabalhem. Ele confia na sua equipe. Lógico, ele os chamou. Se você vai escolher com quem quer trabalhar, com certeza escolheria adultos responsáveis e não crianças que precisam de supervisão o tempo todo. O diretor Fury não chega para o grupo e pergunta: “E ai, como estamos?”

A verdade é que não existe um líder, pois um vez que o grupo está motivado, o grupo age como um time de verdade, ou seja, todos assumem as responsabilidades e todos confiam nos outros. Quem dá as ordens? O Capitão América? Sim. O Homem de Ferro? Sim. Até mesmo o Gavião Arqueiro (de Hawkeye para Gavião… putz…) dá ordens. E em nenhum momento a equipe briga por estar recebendo ordens, até mesmo Tony Stark aceita as ordens que recebe.

Nem mesmo as diferenças entre a equipe atrapalham quando eles estão motivados. Clint e Natasha não tem super poderes, mas ainda assim a equipe sabe o valor que cada um deles tem. Nenhum deles se acha superior aos outros, independente de super poderes ou qualquer outra coisa. Preciso citar algo sobre Gerentes de Projetos vs Desenvolvedores? Não né?

Sabe aquela situação em que alguém diz: “Precisamos colocar uma pessoal responsável por tal atividade” , ele está dizendo na verdade que: “Alguém precisa ser culpado caso não dê certo”. A necessidade de um líder grita na cara da equipe que eles são irresponsáveis e que alguém precisa ficar puxando a orelha deles.

Sabe aquela técnica dos 5 Porquês [4]? A ideia é perguntar várias vezes “porquê” até achar a causa raiz do problema. Mas muitas organizações, inclusive o nosso governo, trabalham com a técnica dos 5 Quem. Perguntar várias vezes quem até achar um estagiário para culpar.

Talvez a formação militar do Capitão América dê a ele mais condições de organizar taticamente o grupo, e por isso o grupo ouve o que ele tem a dizer, isso é ser um líder. No entanto, quando o Gavião está no topo do prédio ele possui uma visão melhor, por tanto ele organiza a equipe, tomando para si a responsabilidade da decisão e agindo como um líder.

Esta frase define bem o filme: “This scene wasn’t a 15-year-old fanboy’s dream. And it wasn’t a really great Halloween party in Hollywood, but something much bigger.” [2]

E você? Prefere trabalhar com os Vingadores, ou ter sua certificação Scrum Master?

Refrências

[1] Valores do Extreme Programming, http://improveit.com.br/xp/valores

[2] ‘The Avengers’ enjoy benefits of teamwork, http://www.suntimes.com/entertainment/movies/12131098-421/the-avengers-enjoy-benefits-of-teamwork.html 

[3] Gestão Moderna, http://blog.aspercom.com.br/2012/03/20/gestao-moderna/

[4] 5 Porquês? http://ogerente.com/logisticando/2007/02/02/5-porques/