sábado, 27 de novembro de 2010

Mais sobre TDD e coding dojo

Um post sobre o uso de baby steps em dojos, escrito pelo Mauricio Aniche, tem causado discussão. Eu escrevi sobre baby steps há algum tempo e concordo em gênero, número e grau com o post, mas há um ponto que ainda não vi ser tocado na comunidade.

Eu tenho observado há alguns meses que algumas práticas do dojo andam meio que passando uma visão incorreta sobre TDD, e não é só por causa das cascatas de ifs. Nada melhor que exemplos para mostrar o que quero dizer. Em TDD/BDD/whatever no mundo real, ninguém vai escrever um teste:
it 'dois mais dois é igual a quatro' do
...
end


ou

def test_quatro_nao_e_um_numero_feliz(self):
...


Nestes códigos, o nome do método é a expectativa. Em TDD, uma expectativa, é o resultado que se espera que ocorra na execução do teste, seja uma expectativa padrão como order.clear |should| change(order.item_count).by(-3) ou uma verificação de mock como MailService.should_receive(:send).with("sos@batcave.com").

O nome do método de teste (ou a sua descrição, no caso do RSpec) não é e nem pode ser a expectativa, pelo menos a se julgar pela literatura de TDD. O nome do método de teste é o nome de uma responsabilidade da classe (o "test method names should be sentences" que é tão enfatizado em BDD).

O correto seria:

it 'soma dois números' do
...
end

ou

def test_identifica_numeros_infelizes(self):
...

Em todos os casos, o nome dos métodos é uma descrição abstrata - no sentido de não trazer exemplos concretos - e as expectativas com seus valores objetivamente verificáveis vêm no interior dos métodos de teste. Ou seja, em TDD, cada método de teste representa algo que a classe é capaz de fazer e não infinitas variações de valores passados a uma mesma funcionalidade. No fim das contas, a expectativa acaba sendo escrita duas vezes: uma vez no nome do método e outra na expectativa propriamente dita. A questão aqui não é o uso ou não de baby steps, mas uma formulação de testes que é conceitualmente diferente daquela utilizada na literatura de TDD.

Veja um exemplo, do livro "Growing Object-Oriented Software, Guided By Tests", de Steve Freeman e Nat Pryce (Addison-Wesley, 2009):

public class CatalogTest {
private final Catalog catalog = new Catalog();

@Test
public void containsAnAddedEntry() {
Entry entry = new Entry("fish", "chips");
catalog.add(entry);
assertTrue(catalog.contains(entry));
}

@Test
public void indexesEntriesByName() {
Entry entry = new Entry("fish", "chips");
catalog.add(entry);
assertEquals(entry, catalog.entryFor("fish"));
assertNull(catalog.entryFor("missing name"));
}
}

Neste código, os testes são:
- contains an added entry
- indexes entries by name


Ou seja, são responsabilidades abstratas e não exemplos concretos.

Kent Beck, em seu cĺássico "Test-Driven Development By Example" (Addison-Wesley, 2003), vai na mesma linha: seus testes na classe Money que foi citada no post se chamam "multiplication", "equality", "currency". Ou seja, nada de "twoDollarsPlusThreeDollarsEqualFiveDollars".

Outra referência de TDD que gosto bastante, "Test Driven: TDD and Acceptance TDD for Java Developers", de Lasse Koskela (Manning, 2007), também segue a mesma ideia. Este livro, por sinal, tem disponiblizado o segundo capítulo para download, contendo um excelente exemplo de baby steps que vale mesmo a pena ser lido. Neste exemplo, novamente, os testes são "unknownVariablesAreIgnored", "multipleVariables" ou "missingValueRaisesException". Novamente, nada de expectativas nos nomes dos testes.

Certamente, não há "culpados" por isto, e também é provável que muitos não vejam assim tanto problema no que expus. Coding dojos são dinâmicos e não hierárquicos por definição, portanto evoluem e se alteram com o tempo e com o contato de diferentes pessoas. Assim, o "desvio" de TDD pode até não ser um problema em si, afinal o coding dojo é um jogo, mas temos que perceber que estamos praticando sob o nome de TDD uma coisa que não é exatamente TDD. E, na verdade, estamos praticando e aprendendo, uma vez que o coding dojo, pelo menos aqui no RJ, tem sido visto como ferramenta de aprendizado de programação. Quando o desenvolvedor está consciente que nos dojos é praticada uma variação do TDD, tudo bem. Porém, no caso de um iniciante, nós temos, sim, um problema, pois as pessoas vão aprender como TDD algo que poderia até mesmo ser considerado um antipattern.

Talvez pelos problemas utilizados em dojos serem bastante simples, muitas vezes com apenas uma feature, a única forma de ter mais de um método de teste acaba sendo a fatoração das classes de teste por expectativas e não por features. Na verdade, não sei nem se isto é um problema para todo mundo. Pessoalmente, é algo que não gosto e que me incomoda, me soa como se o código estivesse pulando e gritando "ei, eu estou errado, você não está vendo?". Mas é o meu ponto de vista. E aí, qual é o seu?

14 comentários:

Hugo Lopes Tavares disse...

Excelente, Rodrigo.

Você acabou de tocar em um ponto em que muitas vezes a gente faz errado achando que está fazendo algo super certo. Daqui pra frente vou me lembrar disso.

O pior é que isso é o bê-a-bá, toda literatura fala como se deve fazer, mas a gente faz errado.
-------

Alguns dos últimos dojos que eu participei, sinceramente, não foram dojos. Eu, assim como vários outros, não estávamos treinando nada, mas ensinando algumas pessoas o básico de programação. Não que isso seja um resultado ruim, mas o objetivo do dojo não é pegar pessoas que não saibam programação, mas sim pessoas que saibam e que estão dispostas a melhorar.

Algumas pessoas no Rio notaram isso, e acabaram criando os ForkInRio da vida. Eu havia conversado com o Álvaro (Turicas) ano passado sobre isso, sobre essa "nivelação". Mas é algo muito complicado, e eu realmente não sei se é possível ter o nivelamento.

Bem, obrigado por apontar isso, e eu espero não errar mais ;-)

Abraços,
Hugo.

Hugo Maia Vieira disse...

Bravo!

Já havíamos conversado sobre isso no nosso Dojo UENF a bastante tempo atrás e quando você expôs a ideia aquele dia, todos concordamos e passamos a utilizar essa abordagem, tanto no dojo quanto em nossos trabalhos.

Concordo também com tudo que o Aniche escreveu em seu post sobre baby-steps, assim como esse seu post de 2008 que também é excelente.

Sobre o dojo concordo plenamente com a colocação de Hugo. Acredito que o dojo seja uma ótima ferramenta de ensino, tanto que temos uma disciplina dojo na uenf onde os calouros estão aprendendo a programar. No entanto, é como o Hugo falou, não quero ir ao dojo apenas para ensinar algumas pessoas o básico de programação, quero realmente treinar. Fabiana Murer não compete no juvenil, pois não teria a menor graça e ela não evoluiria, ela compete com os melhores; ela não treina pulando a mureta do quintal de casa, ela treina tentando sempre o que ela nunca havia conseguido pular.

Parabéns pelo excelente post!
Abraço!

Rogerio Atem de Carvalho disse...

Temos então dois tipos de Dojo? "Learning Dojo" e "Improving Dojo"? Uma pergunta mesmo, se a participação é voluntária, como se faz nesses casos? Chamadas específicas? (desculpe, mais saí tb da discussão central do post)

Diego Manhães Pinheiro disse...

Eu gostaria de complementar o que o Hugo descreveu: Acho muito legal diminuir os passos para que todos possam acompanhar o raciocínio. Quando se parte do pressuposto que todo e qualquer indivíduo é iniciante em programação essa metodologia é muito interessante. Nesse ponto eu acho legal, porque agrega um público que desconhece o Dojo a causa.

Mas NÃO É o intuito do Dojo. Eu acredito que o Dojo não é exatamente agregar pessoas. A palavra Dojo significa o local onde se treina e não necessariamente COMO treinar. Ponto.
Fazer um Coding Dojo funcionar com sucesso envolve um processo de forma que os problemas sejam desafiadores e tenham grau de complexidade favorável. Não se trata de pegar qualquer problema e sair fazendo.
Resumindo : O problema trata-se do processo metodológico e pedagógico utilizado nos Coding Dojos.

Gostaria de reavivar o ponto mais interessante do post que é relaçionado a complexidade ciclomática das operações de uma classe/objeto e da ubiquitous language usada em testes automatizados usando o padrão xUnit . Agrupar testes por responsabilidade é um cuidado pessoal que todo desenvolvedor tem que ter, principalmente porque pra isso não existe "receita de bolo". A fonte de informação mais próxima de uma receita de bolo a meu ver é o livro XUnit Testing Patterns : Refactoring Code, que recomendo a leitura.

Gostaria de aproveitar e compartilhar um vivência pessoal; de tanto ficar seguindo os passos de bebê, algumas vezes me encontrei fazendo passos de "Zigoto", enquanto programava sozinho, ou seja, detectei um retrocesso após ir a algumas seções de Coding Dojo no inicio. Claro que isso foi um interpretação pessoal e errônea da minha parte e já refigurada. :)

Muito legal o artigo. Parabéns !!

Eduardo Hertz disse...

Rodrigo,

Penso da mesma forma que você. No mundo real isso não acontece. O maior problema é essa ideia errada de TDD ser passada para pessoas que não conhecem TDD. É a mesma coisa que você falar pra uma criança que a Terra é redonda, sendo na verdade achatada nos pólos. (não está totalmente errado, mas também não está correto)

E em relação ao que a galera acima falou de realmente TREINAR em Dojos, foram poucos o que eu frequentei e eu sai pensando: "Foi foda esse Dojo".

Sobre baby steps, como o Diego falou, o programador deve estar sempre atento. Nós temos que entender que práticas como baby steps, pair programming, etc, são BOAS PRÁTICAS, não receitas para desenvolver um software. Eu posso fazer um excelente código sem parear com outro programador. Se você não seguir baby steps, não quer dizer que você vai criar um código ruim. Quer dizer apenas que naquele momento você não precisou dessa prática. Em outra situação você pode precisar. Não podemos encarar essas práticas como obrigações e verdades absolutas (claro, algumas práticas são essenciais, como o TDD). Temos que estar sempre nos vigiando, pois, algumas vezes, essas práticas fazem com que a nossa produtividade caia, tornando o desenvolvimento burocrático e chato.

É isso. Parabéns pelo post!!


Abraços!

Álvaro Justen "Turicas" disse...
Este comentário foi removido pelo autor.
Álvaro Justen "Turicas" disse...
Este comentário foi removido pelo autor.
Álvaro Justen "Turicas" disse...

Rodrigo, ótimo post!

Só gostaria de deixar claro que a granularidade dos testes depende do que precisamos implementar. Exemplo: digamos que precisamos criar uma calculadora, então precisamos testar se ela soma corretamente. Digamos que, dentro do teste de soma, queremos testar 5 condições com valores diferentes. Quanto maior esse número de condições, maior fica o tamanho do nosso teste (e, por conseguinte, ele fica mais complexo e difícil de depurar, em caso de falha). O que você sugeriria nesse caso?
Concordo sobre as nomenclatura dos testes que você defende, mas acho que não devemos nos prender a métodos e classes. Por exemplo: crio uma classe que possui os testes de soma (e não somente um método que verifica a soma); dentro dessa classe eu colocaria testes como métodos: soma_numeros_positivos, soma_numeros_negativos etc.
Enfim, acho que a maneira como os testes serão modelados depende muito de quão complexos os casos de testes serão, não somente métodos ou classes - acho que não ficou tão explícito no post.

Sobre o que o Hugo comentou sobre "nivelamento": isso já gerou acalouradas discussões no DojoRio! :-) A conclusão que chegamos aqui é que o Coding Dojo (pelo menos como temos hoje por aqui) é um local inclusivo, então não poderíamos colocar limitações para quem quiser participar. De qualquer forma, vejo que o Coding Dojo não é suficiente para aprendizado, então precisamos criar outros tipos de encontros para suprir as outras necessidades (para a galera mais avançada que quer fazer testes que necessitem de um vasto conhecimento prévio, por exemplo).

Abraços.

Rodrigo Manhães disse...

@hugobr, @hugomaiavieira, @ratembr, @dmpinheiro, @eduardohertz e @turicas,

A questão do nível dos dojos é um assunto complicado. A idéia inicial dos dojos - pelo menos quando começamos aqui em Campos era assim - era mesmo de treinar, pegar problemas desafiantes para trabalhar neles. Era clara a idéia de que coding dojo era uma espécie de prática de aperfeiçoamento para programadores. Nem passava pela nossa cabeça, naquele momento - Hugo Lopes e Diego estavam por lá, podem confirmar - que o dojo poderia ser um instrumento de ensino de programação básica.

A evolução do coding-dojo tem o levado a assumir esse caráter de ensino de programação. Por um lado, é muito bom e gratificante ver pessoas que de outro modo teriam uma relação conflituosa com programação entrando no jogo, participando e se interessando em aprender mais. Por outro lado, a coisa tende a ficar repetitiva e, no limite, sem graça para os veteranos (poucos são os dojos que eu não tenho a solução de cabeça logo que é formulado o problema - e o mesmo deve acontecer com vocês). Eu não sei qual seria a solução. Eu mesmo sinto falta de fazer algo mais complexo em um dojo. A alternativa do fork é insuficiente para matar essa "fome", pois é uma dinâmica completamente diferente com objetivos diferentes e nem de longe tão divertida quanto o dojo. Mas também não me agrada a ideia de elitizar o dojo (na verdade, não me agrada a ideia de elitizar coisa alguma).

Eu quero escrever algo sobre isto futuramente, quando tiver uma opinião mais refletida sobre o assunto.

Obrigado a todos pelos comentários de altíssimo nível e pelos RTs!

Rodrigo Manhães disse...

@ratembr

Na verdade, no começo era um "improving dojo" e com o tempo foi virando um misto com "learning dojo". As fronteiras não são muito claras.

Nessa discussão toda, eu senti um leve aroma de artigo ao longe. Será que procede?

Álvaro Justen "Turicas" disse...

Rodrigo,
concordo com relação ao ForkInRio - são coisas diferentes.
A minha ideia é fazer uma espécie de Coding Dojo (adaptado) para a galera mais hardcore - estou conversando com o pessoal do Instituto de Física da UFF e possivelmente faremos lá algumas sessões voltadas para computação científica, que vai requerer alguns conhecimentos básicos e o foco será em resolver problemas mais avançados.

Rodrigo Manhães disse...

@turicas

Para uma calculadora de soma, eu dividiria a soma em subresponsabilidades, como você escreveu: "somar números positivos", "somar números negativos", "somar números em notação científica", "somar números racionais" e o que mais viesse. Sempre por cenários, responsabilidades, features, nunca fazendo o nome do teste ser a própria expectativa.

Caso se deseje ter mais de uma expectativa para uma mesma responsabilidade, eu normalmente não me incomodo em definir mais de uma no mesmo método de teste, dentro do razoável.

Abraço e obrigado pelo comentário!

Rodrigo Manhães disse...

Turicas,

pois é, criar dois "tipos" de dojos seria mesmo uma boa solução, não excludente. E essa espécie de "divisão em faixas" acaba reforçando a analogia com artes marciais.

Tarsis Azevedo disse...

Sobre Baby steps:

Pra mim é só uma forma de encadear meus pensamentos, evoluir devagar meu raciocinio, só. Se vc é um bebe, seu passo sera menor, se vc é adolescente seu passo ja será outro, e adulto outro! Isso é baby step. Uma forma de encadear raciocinios.,

Quanto ao nivel do dojo, concordo que o nivel as vezes decepciona.

Acho que seria legal ter 2 ou 3 niveis, nao excludente, porem com um aviso: "O nivel aqui é esse. Se vc nao aguenta, nao venha!" ;D

Por fim, APAREÇAM NO DOJO!!! E por consequência o nivel sobe!!! ;D

Abraços