No meu último artigo, comentei bastante sobre a minha opinião de que testes devem ser sobre o relacionamento entre partes específicas do seu código e não sobre interfaces ou contratos. Na minha experiência, os testes mais duradouros e de maior valor são aqueles que exercem as interfaces e contratos indiretamente, através do arquitetura particular oferecida pelos mesmos.
O ressurgimento de testes como uma ferramenta ágil é uma coisa recente e, obviamente, uma excelente oportunidade para conversar sobre técnicas, filosofia e metodologia de desenvolvimento. Em especial, a comunidade Rails tem feito um trabalho excepcional de evangelização sobre o assunto, tornando testes um participante de primeira classe no discurso de desenvolvimento de software.
Entretanto, como é fácil acontecer, o próprio sucesso do assunto está se convertendo em uma fonte de perigos para desenvolvedores iniciantes ou que não tenham tanto familiaridade com TDD e BDD. A própria multiplicação dos pães, digo, dos frameworks de teste está contribuindo para isso no sentido de que, em um afã de criar mais features do que o concorrente, alguns framework estão simplesmente promovendo técnicas péssimas de testes em troca de um falso senso de segurança.
Isso volta um pouco na discussão sobre a diferença entre TDD e BDD, mas acho que o ponto merece uma ênfase. Em resumo, é imprescindível evitar substituir arquitetura, mesmo quando se está fazendo TDD, por meros testes.
Isso fica mais fácil de ser percebido com algumas ilustrações. Tomando o Shoulda como exemplo, é muito comum ver código como o seguinte:
class UserTest < ActiveRecord::TestCase
should_belong_to :account
should_have_many :posts
should_have_named_scope('recent(5)').finding(:limit => 5)
should_have_index :age
end
Esse tipo de código não prova absolutamente nada sobre o desenho próprio de sua classe. O código acima:
É redundante, porque as três primeiras cláusulas já serão testadas automaticamente em outras partes do código, especificamente em controller;
É quebradiço, porque é diretamente relacionado à implementação e não ao comportamento da classe em si;
É pouco mais do que um teste de sanidade para descobrir se o desenvolvedor colocou algumas poucas linhas de código em seu modelo;
Expõe detalhes de implementação, como no caso do matcher para índice.
Em outras palavras, todos os testes acima são absolutamente inúteis. O teste de escopo é o único com algum valor para o teste do modelo em si, mas continua sendo redundante.
Pior ainda, existem exemplos como shouldhavebeforesavecallback, proveniente do Remarkable. Esse é o tipo de asserção que chega a ser contraproducente. É um teste que expõe a funcionalidade subjacente de um modelo, que por regras de encapsulamento deveria ser completamente isolada e invisível para as demais partes da aplicação, é um desvio completo do que TDD representa.
Testes, mais uma vez, são sobre interoperabilidade entre facetas do código. São parte de uma conversa arquitetural que procura se focar o mínimo em detalhes internos de implementação. O objetivo é escrever o menor corpo possível de testes–axiomas–que possa dar uma indicação da validade de um dado corpo de código. E como eu repito freqüentemente aqui, simplicidade é um alvo explícito de boas arquiteturas.

Muito bom Ronaldo! Mas por outro lado, você não deveria dar exemplos de como deve ser feito também?
Se você já apontou o problema, poderia apontar uma solução!
Ah, e eu discordo que os três primeiros testes sejam redundantes devido ao fato que eles serão testados no controller.
Por exemplo no rspec, como bem se sabe, no controller devemos usar mocks!
Concordo com você, para mim teste tem que ser sobre a entrada e a saida. E não se preocupar como a entrada gerou a saida.
Daniel, com certeza.
Esse foi um artigo pequeno e rápido sobre o problema em si. Vou emendar um na seqüência sobre boas práticas.
“Como bem se sabe” é um convite para não discutir. Mocking é uma escola. Não é necessariamente uma verdade. Esse é um dos pontos em que o pessoal mais tem problema hoje. Mock em tudo sem testes reais.
—
Leonardo, exato.
Manter o foco na arquitetura muda bastante as regras do jogo, e esses teus últimos 2 posts foram um verdadeiro balde de aguá fria na forma como eu estava construindo meus testes aqui.
Assim como o Daniel, também aguardo pequenas demonstrações praticas daquilo que você considera como sendo a forma “mais correta”
Abraço,
Tailor.
Opa! Não precisa se assustar tanto.
O exemplo foi bem particular e não significa que testes de modelo são absolutamente inúteis em si. Os exemplos acima são, mas testes de modelo–ou unitários, usando a palavra correta–tem seu valor desde que usados para testar funcionalidade real. O problema é que muita gente só testa aquilo e deixa como a funcionalidade toda estivesse coberta.
Existe um ponto de equilíbrio entre o uso da funcionalidade e a descrição da funcionalidade que pode ser atingida de maneira bem fácil. Basta começar pela preocupação arquitetural mais alta e ir descendo. Isso dá uma boa indicação de quais são os valores necessários para os testes.