7. Red / Green / Refactor Write a test for new capability Start Compile Fix compile errors Run the test And see it fail Write the code Run the test And see it pass Refactor as needed
TDD promove a criação de código testável. Esta é uma característica importante do código e relaciona-se com: Forte Coesão: Classes coesas -> responsabilidades bem definidas -> testes sintéticos Loose Coupling: Fácil de testar classes isoladamente Baixa redundância: 1 Teste por responsabilidade Encapsulamento: Testes identificam os métodos e propriedades que têm de ser expostos (ajudam a definir a interface) Todas estas características são o resultado de boas práticas de desenvolvimento, pelo que o desenvolvimento de bons testes resulta em melhor código.
Um factor que é fácil de observar à medida que ganhamos mais prática a desenvolver código de forma Test-Driven é que é mais simples desenvolver testes para componentes do “interior” do sistema do que para componentes que se encontram na “orla” do sistema. São conhecidas as dificuldades em implementar testes para componentes que lidam com Web Services, Componentes de Acesso a Dados ou interfaces gráficas. Existem algumas técnicas que nos ajudam a lidar com estes desafios.
Endo-Testing é uma técnica que consiste em eliminar dependências substituindo objectos dependentes por objectos de teste chamados Mock objects. Mas qual é exactamente a vantagem de criarmos um novo objecto e substituir objectos que fazem parte do domínio por estes objectos de teste? Quando os componentes que pretendemos testar se encontram na “orla” do sistema, as dependências que estes possuem são geralmente de sistemas externos. Por vezes estes sistemas não se encontram disponíveis no ambiente de desenvolvimento ou então produzem resultados não determinísticos. Para termos confiança no resultado dos testes estes têm de ser determinísticos. Nestes casos, os testes devem ser realizados de forma a conseguirmos testar os componentes do sistema isoladamente. Uma solução é utilizar Mock Objects.
Imaginemos que tempos uma classe Money com um método estático GetRate() que deve obter uma taxa de câmbio de um Web Service. A questão que se coloca é como é que conseguimos implementar um teste para o método GetRate() da classe Money se o resultado do Web Service é imprevisível (seja porque não está disponível no ambiente de desenvolvimento, ou porque os valores devolvidos pelo Web Service variam constantemente)?
A solução consiste em substituir nos testes o proxy do Web Service por um Mock do mesmo. No entanto para conseguirmos criar um Mock Object necessitamos de ter uma interface.
Apesar das vantagens, a utilização de Mock Objects apresenta também alguns desafios. Em primeiro lugar, no caso de serem Mock Objects estáticos, resultam em mais classes para manter. No entanto, o maior desafio consiste em como quebrar o encapsulamento de forma a substituir o objecto real pelo Mock.
São particularmente difíceis de testar e, consequentemente, difíceis de implementar de forma test-driven. É importante considerar que não basta conseguir definir um teste, é necessário que o próprio processo de implementação do teste e desenvolvimento do código UI correspondente seja simples para que o tempo de turn-around não desencoraje a prática de TDD.