O que Test-Driven Development não é

Uma dúvida muito pertinente em engenharia de software é sobre Test-Driven Development, TDD para os mais chegados. Há resistência por parte daqueles que não conhecem - ora, também não é para menos: o nome remete a Teste e teste remete a um processo da área de Verificação e Validação.

Após ouvir o developer dizer nunca fez e nunca viu “fazerem TDD” e suas argumentações sobre o porque isso não funciona, costumo perguntar uma simples pergunta: para você, o que é TDD ?

Até o momento as respostas foram uma derivação de: “Serve ahn… pra você verificar se seu software funciona (..) serve para evitar bugs”. Ora, não é para menos que você desacredita - e por isso nunca tentou - na ideia.

Redução de bugs é um efeito do TDD e não seu objetivo. Alguns já não definem mais a sigla como Test-Driven Development, mas sim como: Test-Driven Design o que na minha opinião é muito mais descritivo para os “novatos no assunto”. Este novo nome deixa tudo mais claro: TDD é sobre a construção do seu design. Pronto. Só isso.

Partindo do princípio que você saiba o que “design” significa em engenharia de software, aposto que está repensando em tudo que você argumentava (se é que) contra a adoção do estilo de desenvolvimento. Explicado o que é o Test-Driven Development Design há ainda outras barreiras a ser vencidas. Vamos a elas.

1. TDD consome mais tempo

Depende. No início é óbvio que você irá mais devagar, pois estará aprendendo algumas coisas enquanto tenta implementar uma feature:

  • A técnica de codar seguindo TDD;
  • A técnica de codar Testes Unitários de Unidade;
  • O framework de teste;

Codificar seguindo o Test-Driven Design (vou repetir isso inúmeras vezes para fixar), após estudar e pegar prática, você irá produzir mais do que antes. Fato. Além disso, provavelmente o deixará pensando bastante sobre pontos como:
como devo começar meu teste? O que devo testar? Como testar? e, até onde testar?

Note que “testar” neste contexto significa: verificar que o método está fazendo o que se espera que seja feito; Apesar de parecer óbvio, é melhor ter isso sempre em mente.

Teste de Unidade é um ponto importantíssimo para conseguir seguir em frente com o Test-Driven Design. Isso dá um post dedicado, por isso podemos discutir sobre o que não é Teste de Unidade.

Ele não “testa” necessariamente todos os métodos da sua classe. Um erro comum é achar que Teste de Unidade significa testar todo getter e setter de uma classe. Errado.
Quando estiver testando um método de uma classe e este método chamar outro método de outra classe, você não pode deixar essa chamada acontecer “de verdade”. Aqui entra o assunto Test Double - double, mock e stub. Também digno de outro post. Quando eu digo outro método de outra classe, estou literalmente dizendo que:

class Foo
  def do_something(another_class: MyClassDependency.new)
    # …
    another_class.find_bar(…)
  end
end

o MyClassDependency#find_bar precisa ser “mockado" ou “stubado”, pois devemos sempre partir do princípio de que ele já foi testado isoladamente e o find_bar irá retornar o objeto/valor corretamente. Por isso o Mock/Stub. (vide imagem do post ;])

Não devemos testar métodos privados diretamente. Dizem que há exceções, realmente podem haver, pois design é questão de experimentar e evoluir, porém eu em particular nunca testei método privado - mesmo em algumas situações eu ter insistido e quase feito.

O motivo é muito simples: devemos encarar nossos métodos privados como versões instáveis da nossa API. Considere toda classe do seu projeto, como uma API: métodos públicos são o canal de comunicação com a classe que tem suas interfaces (nome do método, parâmetros e retorno) estáveis. Já os métodos privados da classe, são nossos métodos instáveis, ou seja, estamos informando ao cliente (outra classe do projeto) que aquele método poderá mudar sua interface de uma versão para a outra sem aviso prévio. Isto não quer dizer que seus métodos privados não serão testados. Muito pelo contrário. Você precisa testar o método público da sua classe que utiliza o método privado em questão. Com isso, você garante não só que seu método instável (privado/protected) está funcionando, mas também que ele está bem relacionado com o(s) método(s) público(s) que o utiliza. E é perfeitamente normal sua classe possuir alguns métodos privados. Não tenha medo de utilizá-los, sempre pensando que um método deve ter apenas uma responsabilidade - um motivo para mudar.

Novamente: Teste de Unidade não deve chamar métodos de classes colaboradoras (igual exemplo acima). Ou seja: não devemos chamar nossa API/abstração de banco de dados, nem aquele Webservice de Pagamento/Notificações, nem o seu Mailer, nem seu Filesystem, etc. Você sempre deverá Mockar ou Stubar essas “dependências” da sua classe.

2. Eu gosto de “Testar” depois.

Há quem diga que já tem em mente o que precisa fazer para aquela feature funcionar e que por isto, prefere fazer o código de produção [1] primeiro e o teste depois.

Essa é fácil: se for para fazer o teste depois, você não estará construindo o design da sua aplicação. Estará perdendo tempo.

Testar depois tem dois problemas: a) perderá a chance de desacoplar suas classes, pois acredite: TDD faz você pensar mais sobre como fazer e isso incrivelmente nos faz mais produtivos, pois você resolverá os problemas de forma mais simples. b) você fará o teste assumindo que sua classe/método está correto e funciona, com isso criará o que chamam de Teste Viciado que em outras palavras quer dizer: você faz seu teste satisfazer seu código de produção, porém seu código de produção não garante que o teste está correto.

Dica simples: não deixe para testar depois. Crie o costume de fazer o teste antes!

[1]: Código de produção é o código que você deploya.

3. Minha equipe/co-workers não tem interesse pelo assunto.

Você não precisa esperar seu chefe instaurar a obrigatoriedade do Test-Driven Design. Não espere que ele saiba das vantagens disso. Aproveite o momento para você começar a espalhar a ideia na equipe.

Recentemente entrei numa equipe que não faz testes de unidade, tão pouco TDD. No começo, fiquei observando a reação das pessoas sobre Test-Driven Design e suas queixas sobre a possibilidade de fazer. Minha meta era em um mês conseguir mostrar o que a técnica de teste poderia resolver nos projetos e o quão empolgante é codificar com Test-First. Passado este um mês, eu já tinha mostrado TDD in Action para três desenvolvedores que além de ficarem convencidos e animados com a ideia, passaram a defendê-la. Eu acredito que mudanças de cultura deste tipo, devem sempre ser bottom-up, dos developers para os gestores. Muito improvável que seu gestor não aprovará a ideia se você se propuser a explicar para que o Test-Driven Design é executado em todo o mundo.

Concluindo

Test-Driven Development Design é sobre como construir seu design de forma simples e desacoplada. Sem bullshits, sem bala de prata ou santo graal. Adotar Test-Driven Development é evoluir sua forma de fazer software de uma forma muito rápida.

Hélio Costa

Desenvolvedor de software há um tempinho. Interessou-se por Object-Oriented Design e Test-first antes do Eddy Merckx ser removido na organização da UCI Road World Championships.

Sao Paulo, Brazil