Posts com o tag ‘tdd’

30abr2010

1st International Workshop on Test-Driven Development (TDD 2010)

(2) comentários

No dia 11 de abril participei do 1st International Workshop on Test Driven Devevelopment (TDD2010), realizado em Paris. Fui apresentar meu paper entitulado “Most Common Mistakes in TDD Practice: Results From An Online Survey With Developers” (que pode ser baixado aqui). A plateia contava com pessoas de renome na comunidade como Michael Feathers, Steve Freeman, Laurie Williams, David Janzen, John Clements, entre outros.

O keynote foi feito pelo Steve Freeman, autor de um dos melhores livros de OO e TDD que já li (Growing Object-Oriented Software, Guided by Tests). Ele basicamente mostrou seu ponto de vista sobre TDD. Segundo ele, “programação comum está para TDD assim como programação procedural está para programação orientada à objetos”. Foco bem definido, bom feedback e progredir sem medo também foram citados como vantagens. Uma citação muito interessante, e que venho pensando há muito tempo sobre como transmitir essa idéia é a de que “o programador deve entender o porquê TDD funciona; caso contrário, é apenas burocracia”.

Em seguida, o aluno de mestrado Theodore Hellman nos apresentou a ferramenta que seu grupo desenvolve em Calgary, Canadá. Ela testa interfaces de uma maneira muito interessante: antes de desenvolver cada interface, o programador faz protótipos na ferramenta, desenhando caixas de texto, botões (parecidos com os que desenhamos no papel). A mágica acontece depois que o protótipo está pronto: você testa o protótipo, clicando nos botões e digitando valores nas caixas de texto fictícias, e a aplicação os executa na aplicação real! O problema é que funciona apenas para aplicações .NET Windows Forms.

Em seguida a apresentação do John Clements (um dos criadores do Dr. Scheme) e do David Janzen (professor da Politecnica da California, possui muitas publicações relacionadas a experimentos com TDD) sobre o quão difícil é ensinar TDD para os alunos. Apresentaram algumas técnicas de ensino e problemas que já tiveram nas primeiras aulas sobre o assunto.

O alemão Florian Barth, da Universidade de Manheim, mostrou a ferramenta para testes de aceitação que seu grupo trabalha. É uma mistura de Fitnesse com linguagem de programação, onde você escreve não só os casos de testes como a implementação do teste em planilhas. É bem interessante, já que basicamente elimina o trabalho de codificação dos testes. O problema é que algumas coisas ainda são um pouco complicadas e exigem um trabalho extra na planilha. Mas sem dúvida é um projeto para ficar de olho.

Robert Chatney apresentou seu framework LiFT, uma DSL para testes de aceitação em Java, inspirada no Cucumber. Ela faz com que seus testes em Java fiquem muito fluentes. Totalmente extensível, é um projeto que pretendo colaborar em breve. O código pode ser encontrado no Google Code.

Em seguida, apresentei meu paper sobre erros (ou desvios) que os programadores cometem quando praticam TDD. Apesar de algumas críticas em relação à metodologia (problemas esses que já eram conhecidos e estavam na seção de “ameaças a validar” do paper), as ideias ali foram elogiadas e todos concordaram com os problemas levantados pelo artigo. Lembrando que esse artigo é apenas um trabalho em andamento, parte da minha dissertação de mestrado. Espero postar partes dela em breve.

Chris Agmen-Smith apresentou sua experiência em um projeto real com ATDD, e fez questão de mostrar que é possível fazer ATDD sem grandes custos, mas com muitos benefícios.

No final do dia, Raj Mudhar solicitou ajuda para pesquisar na área de ATDD em projetos de grande porte. Qualquer empresa que tenha projetos grandes pode participar, divulgando seus dados e juntos publicarem os resultados em conferências de peso.

Resumindo, o evento foi muito muito bom. Um dia inteiro com riquíssimas discussões sobre as mais variadas pesquisas em TDD. Além disso, pude validar muitas ideias com pessoas realmente influentes na área. Um ponto muito interessante é que não estamos tão longe do que eles praticam no dia-a-dia, mas ainda temos um longo caminho a percorrer!

Até a TDD2011, em Berlin! :)

O post original pode ser encontrado em http://www.aniche.com.br/2010/04/1st-international-workshop-on-tdd-2010/

16abr2010

TDD realmente ajuda?

(0) comentários

Geralmente um programador que nunca praticou TDD tem essa dúvida: será que TDD realmente ajuda na qualidade do código? E na redução de defeitos? Ele aumenta ou diminui a produtividade, afinal? Mas como toda e qualquer prática em engenharia de software, é muito difícil avaliar e chegar a uma conclusão exata sobre seus ganhos e benefícios.

Nos últimos anos, a comunidade acadêmica vem rodando diversos experimentos para tentar mostrar de maneira empírica que TDD realmente ajuda no processo de desenvolvimento de software. Alguns desses estudos são feitos por professores bastante conhecidos na comunidade, como a prof. Laurie Williams (North Carolina State University) e o prof. David Janzen (California Polytechnic State University).

Algumas dessas pesquisas investigam o fato de TDD reduzir o número de defeitos de um software; já outros investigam o fato de TDD produzir código de melhor qualidade. Alguns até pesquisam por indícios de aumento de produtividade.

Estudos na indústria

Janzen [5] mostrou que programadores usando TDD na indústria produziram código que passaram em aproximadamente 50% mais testes caixa-preta do que

o código produzido por grupos de controle que não usavam TDD. Além do mais, o grupo que usava TDD gastou menos tempo debugando. Janzen também mostrou que a complexidade dos algoritmos era muito menor e a quantidade e cobertura dos testes era maior nos códigos escritos com TDD.

Um estudo feito pelo Maximillien e Williams [6] mostrou uma redução de 40-50% na quantidade de defeitos e um impacto mínimo na produtividade quando programadores usaram TDD.

Outro estudo feito por Lui e Chan [7] comparando dois grupos, um utilizando TDD e o outro escrevendo testes apenas após a implementação, mostrou uma redução significante no número defeitos. Além do mais, os defeitos que foram encontrados eram corrigidos mais rapidamente pelo grupo que utilizou TDD. O estudo feito por Damm, Lundberg e Olson [8] também mostra uma redução significante nos defeitos.

O estudo feito por George e Williams[9] mostrou que, apesar de TDD poder reduzir inicialmente a produtividade dos desenvolvedores mais inexperientes, o código produzido passou entre 18% a 50% mais em testes caixa-preta do que códigos produzidos por grupos que não utilizavam TDD. Esse código também apresentou uma cobertura entre 92% a 98%. Uma análise qualitativa mostrou que 87.5% dos programadores acreditam que TDD facilitou o entendimentos dos requisitos e 95.8% acreditam que TDD reduziu o tempo gasto com debug. 78% também acreditam que TDD aumentou a produtividade da equipe. Entretanto, apenas 50% acreditam que TDD ajuda a diminuir o tempo de desenvolvimento. Sobre qualidade, 92% acreditam que TDD ajuda a manter um código de maior qualidade e 79% acreditam que ele promove um design mais simples.

Nagappan [12] mostrou um estudo de caso na Microsoft e na IBM e os resultados indicaram que o número de defeitos de quatro produtos diminuir entre 40% a 90% em relação à projetos similares que não usaram TDD. Entretanto, o estudo mostrou também TDD aumentou o tempo inicial de desenvolvimento entre 15% a 35%.

Langr [10] mostrou que TDD aumenta a qualidade código, provê uma facilidade maior de manutenção e ajuda a produzir 33% mais testes comparados a abordagens tradicionais.

Estudos na academia

Um estudo feito por Erdogmus et all [11] com 24 estudos de graduação mostrou que TDD aumenta a produtividade. Entretanto nenhuma diferença de qualidade no código foi encontrada.

Outro estudo feito por Janzen[13] com três diferentes grupos de alunos (cada um deles usando uma abordagem diferente: TDD, test-last, sem testes), mostrou que o código produzido pelo time que fez TDD usou melhor conceitos de orientação a objetos e as responsabilidades foram separadas em 13 diferentes classes enquanto que os outros times produziram um código mais procedural. O time de TDD também produziu mais código e entregou mais features. Os testes produzidos por esse time teve duas vezes mais asserções que os outros e cobriu 86% mais branches do que o time test-last.  Além do mais, as classes testadas tinham valores de acoplamento 104% menor do que as classes não testadas e os métodos eram, na média, 43% menos complexos do que os não-testados.

O estudo de Müller e Hagner [17] mostrou que TDD não resulta em melhor qualidade ou produtividade. Entretanto, os estudantes perceberam um melhor reuso dos códigos produzidos com TDD.

Steinberg [15] mostrou que código produzido com TDD é mais coeso e menos acoplado. Os estudantes também reportaram que os defeitos eram mais fáceis de serem corrigidos.

O estudo do Edwards [16] com 59 estudantes mostrou que código produzido com TDD tem 45% menos defeitos e faz com que o programador se sinta mais a vontade com ele.

Conclusão

A maioria dos experimentos feitos tanto na indústria quanto na academia mostram que TDD melhora o processo de desenvolvimento de software, aumentando a qualidade do código, reduzindo o número de defeitos, diminuindo o tempo gasto com depuração e até aumentando a produtividade dos desenvolvedores.

Entretanto, mais experimentos devem ser conduzidos, levando em consideração diferentes fatores de influência que existem em um ambiente de desenvolvimento de software.

Referências

Podem ser encontradas aqui.

Post original em http://www.aniche.com.br/2010/04/tdd-realmente-ajuda/

18fev2010

Não escreva código novo sem antes ter um teste falhando

(0) comentários

O título desse post é uma frase de Kent Beck, autor do livro Test Driven Development: By Example. A idéia é que você sempre escreva testes antes de implementar qualquer código. Após o teste escrito falhar, você implementa o suficiente para fazer o teste passar. Com os testes passando, você está livre para refatorar (tanto implementação, quanto teste). A partir daí você cria um novo teste e segue o mesmo fluxo. Esse ciclo se repete até você ter toda a funcionalidade deseja implementada, ou seja, ter testes para todas as possibilidades da sua implementação.

Este é o “bê-a-bá” de TDD, mas na prática isso dificilmente acontece. Não porque não queremos fazer testes (se você não quiser escrever testes, o problema é todo seu), mas porque somos exímios programadores, desenvolvemos orientados a testes por anos, e não precisamos mais seguir os baby steps (passos de bebê), afinal somos programadores maduros.

Sendo assim, pulamos etapas: codificamos primeiro para depois escrever os testes, refatoramos mesmo com testes ainda não passando, escrevemos mais testes mesmo tendo testes anteriores falhando, e por aí vai.

Cuidado! Por mais que você seja um programador “fodão”, ainda sim você pode deixar de testar alguma coisa. Uma lógica de negócio, uma alternativa de fluxo ou uma condição de erro podem passar desapercebidas ao se pular as etapas básicas de TDD. Esse teste faltando, por mais simples que seja, pode causar um erro em ambiente de produção e causar transtornos para o cliente e/ou usuário final da sua aplicação.

Vamos utilizar como exemplo uma simulação de pareamento, onde uma dupla de desenvolvedores irá criar um método chamado positive_balance? para dizer se uma conta bancária, representada pela classe BankAccount, possui saldo positivo.

A linguagem utilizada será Ruby e o framework para testes será RSpec.

Os programadores são Félix (piloto do pareamento) e Péricles. Os dois concordam em iniciar criando a classe BankAccount com a declaração do método positive_balance? sem nenhuma implementação:

class BankAccount
  def positive_balance?

  end
end

- Legal, agora vamos escrever nosso teste. – diz Péricles.
- Para uma conta bancária possuir fundos é nessário que seu saldo seja maior que zero.

describe BankAccount do
  it "should have positive balance" do
    account = BankAccount.new
    account.value = 100.00
    account.positive_balance?.should be_true
  end
end

Eles rodam o teste:

F
1)
NoMethodError in 'BankAccount should have positive balance'
undefined method `value=' for #
./spec/bank_account_spec.rb:6:
Finished in 0.010015 seconds
1 example, 1 failure

E o resultado com erro diz a eles que não existe um atributo value na classe BankAccount. Félix e Péricles o criam:

class BankAccount
  attr_accessor :value

  def positive_balance?

  end
end

E executam o teste novamente:

F
1)
'BankAccount should have positive balance' FAILED
expected nil to be true
./spec/bank_account_spec.rb:7:
Finished in 0.010605 seconds
1 example, 1 failure

O teste falha. Então chegou a hora de escrever código novo, a implementação da funcionalidade que eles querem. Félix implementa o suficiente para o teste passar.

class BankAccount
  attr_accessor :value

  def positive_balance?
    true
  end
end

Péricles discorda totalmente.
- Cê tá louco, mano?! Vai retornar true para tudo?! O cara vai ter sempre saldo na conta?

Félix argumenta.
- A gente não precisa escrever código suficiente para o teste passar? Isso é suficiente.

E roda o teste:

.
Finished in 0.009987 seconds
1 example, 0 failures

- Viu? Passou. – finaliza Félix.
- Mas isso é muito baby step. – reclama Péricles – Vamos implementar o código real, ou seja:

class BankAccount
  attr_accessor :value

  def positive_balance?
    self.value > 0
  end
end

- Mas por que vamos implementar isso agora? Afinal nossos testes estão passando. – Félix rebate.
- Porque está na cara que esse código retornando true sempre não funciona.

Félix continua forçando a discussão.
- Como não funciona? Funciona sim, os testes estão passando.
- Funciona, mas a implementação está errada. – diz Péricles.
- Errada? Mas atende os requisitos até o momento. Afinal, os testes são para assegurar que a lógica do negócio está sendo cumprida.

Péricles fica pensativo.
- Mas o único teste que fizemos não está cobrindo todos os casos da lógica.
- Concordo com você, Péricles. E o que devemos fazer agora então?
- Devemos escrever um teste em que a conta bancária não irá ter fundos.
- Exatamente! – confirma Félix.

E eles continuam nesse linha de raciocínio até o final do pareamento.

Não estou aqui dizendo que você tem que sempre seguir à risca o Red Green Refactor do TDD, muito menos usar baby steps toda vez que você codificar (afinal, a vida não é um dojo), mas que você tenha atenção e controle do que está fazendo, tento o domínio da funcionalidade que está implementando.

Uma das maneiras de se conseguir isso é com pareamento. Seu par irá lhe ajudar a não deixar escapar nenhum teste. Outra maneira é com inspeção de código. De repente, outro desenvolvedor que não participou da implementação pode enxergar algo que você (e/ou seu par) não viu.

De qualquer forma, seja humilde. Use as etapas de TDD para funcionalidades ou lógica mais complexas. E também fique livre para burlar as regras para implementar coisas simples e funcionalidades básicas, ou quando estiver bastante à vontade e seguro do que está fazendo. Mas nunca, eu disse nunca, deixe de escrever os testes.


Post original:
http://prodis.pro.br/2010/02/16/nao-escreva-codigo-novo-sem-antes-ter-um-teste-falhando

.

23nov2009

Desprenda-se de convenções de nomenclatura em nome de testes

(2) comentários

Eu compartilho da opinião de Jimmy Bogard, que diz que os nomes dos testes precisam descrever o que e o porque, a partir da perspectiva do usuário, onde o desenvolver possa ler o nome do teste e claramente entender o comportamento que é esperado.

Um teste unitário nada mais é que um método em uma classe, e tanto em C# como Java, existem convenções de nomenclatura de métodos.

Em C#, nome de métodos são declarados utilizando Pascal Case:

[TestMethod]
public void ProductShouldHaveAtLeastOneCategory()
{
  //Test implementation.
}

Já em Java, convencionou-se escrever métodos utilizando Camel Case:

@Test
public void productShouldHaveAtLeastOneCategory() {
  //Test implementation.
}

Muitas vezes, o nome desses testes (métodos) ficam um tanto longos, como os exemplos acima. Dessa forma, a legibilidade não é muito boa.

Seguindo um dos conselhos de Neal Ford, em sua apresentação 10 Ways to Improve Your Code, você pode deixar de lado as convenções de nomenclatura da linguagem em favor da legilidade dos nomes dos seus testes. Escreva o nome do teste como se fosse uma frase, nada de letras maiúsculas para cada palavra, e use “_” (underscore) para separar as palavras.

Veja como fica o exemplo acima em C#:

[TestMethod]
public void Product_should_have_at_least_one_category()
{
  //Test implementation.
}

E agora em Java:

@Test
public void product_should_have_at_least_one_category() {
  //Test implementation.
}

Não há nenhum mal em se desprender das convenções de nomenclatura de C# e Java em prol da legibilidade dos nomes dos testes. Afinal, testes são uma documentação executável e nós queremos uma documentação clara para nosso código.


Post original:
http://prodis.pro.br/2009/11/21/desprenda-se-de-convencoes-de-nomenclatura-em-nome-de-testes

.

1jul2009

.Net Architets Day – Boas práticas de arquitetura e engenharia de software

(1) comentário

dotnetarchitets No último sábado (27/06) rolou o .Net Architets Day 2009 voltado para arquitetura de software (com foco em .Net).

Pouco ou quase nada se falou sobre uso de alguma ferramenta específicas Microsoft, até porque o foco do grupo que já existe há algum tempo é exatamente a utilização do .Net com práticas de engenharia de software e arquitertura.

O evento, segundo a organização, não teve fins lucrativos e o valor foi revertido em brindes e coffe break além de algumas outras despezas (achei muito bacana a prestação de contas apresentada pelo Giovanni, idealizador do grupo).

Estes foram os temas apresentados, com um pequeno resumo de cada palestra. De acordo com a organização, as apresentações e as filmagens estarão disponíveis no site do grupo em breve.

Programando com prazer com Domain Driven Design (DDD)Giovanni Basi

A principal preocupação do arquiteto ou desenvolvedor de software deve ser com o futuro, ou seja, a “manutenibilidade” do sistema. Com o design baseado na lógica do domínio do cliente (DDD), tudo fica mais fácil, desde a forma de se comunicar (linguagem ubíqua) até a modelagem, desenvolvimento e manutenção do software.

Utilizando Injeção de dependência com Unity (Enterprise Library)Leandro Daniel

O acoplamento é um problema enorme em POO. Fazer testes em uma classe de negócio que dependa de uma outra que envia e-mail, por exemplo, ou adicionar uma nova funcionalidade a um pedaço que já esteja amarrado a uma implementação é triste demais. O Unity, um dos blocos da Enterprise Library (incorporado a partir da versão 4.0), foi criado para ajudar nessa tarefa injetando a dependência quando necessário. No entanto, é preciso avaliar o uso para não tornar seu software ainda mais acoplado :)

ASP.Net MVC: tome seu HTML de voltaVictor Cavalcante

Nesta palestra foi feita uma comparação entre o ASP.NET MVC e Web Forms, mostrando como ficamos como, com este último, muito presos à interface e fica difícil fazer testes enquanto que, com MVC separa a lógica da apresentação ficando mais flexível e testável. Por outro lado, MVC tem um preço, é necessário colocar a mão na massa, controlar o HTML, é necessário saber programar para web.

ORM – Sendo preguiçoso com NHibernateJuliano Oliveira

No início da palestra o Juliano trocou o título para “Sendo produtivo com NHibernate”. Este que é um dos frameworks mais conhecidos para mapeamento de objeto relacional é de grande valor na hora de desenvolver seu sistema e diminui bastante a dor de cabeça com criação de tabelas, colunas, constraints, etc. na base de dados. Foi mostrado também o NHProf uma ferramenta comercial para que funciona como o Profiler do SQL Server.

Testes: garantindo que seu código faz o que você querMauricio Aniche

Uma frase que eu tenho ouvido muito ultimamente, mas, graças a Deus, a grande maioria das vezes, só como ilustração “Tá pronto, só falta testar”, foi mencionada mais uma vez. E não é difícil ouvir isso nas empresas que ainda não adotam boas práticas de desenvolvimento. Foi feita uma bela apresentação de como os testes podem ajudar tanto a evitar bugs como na qualidade e na própria codificação, pois fica fácil entender a lógica e as regras de negócio fazendo testes. O uso de ferramentas de automatização de testes como o NUnit ou o Próprio MSTest do Visual Studio, o uso de frameworks de mock potencializam a produtividade.

Valeu muito a pena participar e já estou esperando o próximo.