quarta-feira, 2 de abril de 2008

Desacoplando para melhorar o design e facilitar os testes

Usando TDD, normalmente nos deparamos com situações em que precisamos mudar nossa implementação para atender a algum teste, isso é normal e viável, já que os testes são parte do sistema.

Quando o Paulo Silveira veio a Fortaleza e ministrou um workshop sobre Java, falou-se muito sobre desacoplar, não usar métodos estáticos (sempre que possível) e outras boas práticas como IoC, acontece que venho buscando sempre soluções pra esses casos sem mudar minha implementação, na maioria até consegui, lógico que com maior dificuldade e esforço.

Assim, esta semana discutimos a implementação de alguns testes que estavam muito complicados e ví que o que o Paulo havia falado era bem simples, ao menos da maneira que solucionamos o problema. Vou ilustrar com um exemplo bem simples pra deixar claro como podemos facilitar os testes desacoplando algumas coisas.

Primeiro quero dizer que com um Spring da vida você não vai precisar usar esse artifício, pois com o IoC do Spring você pode fazer isso;
Segundo, é importante ficar atento, porque se existirem muitas dependência, um artifício simples como esse pode não funcionar.

Ao exemplo:

Tenho 3 classes.
0: public class ClasseA {
1: public void metodo1(){
2: ClasseB classeB = new ClasseB();
3: classeB.metodo2();
4: }
5: }
0: public class ClasseB {
1: public void metodo2(){
2: System.out.println(ClasseUtil.m1());
3:
4: ClasseUtil classeUtil = new ClasseUtil();
5: System.out.println(classeUtil.m2());
6: }
7: }
0: public class ClasseUtil {
1: public static String m1(){
2: return "m1";
3: }
4:
5: public String m2(){
6: return "m2";
7: }
8: }
No teste da ClassseB terei algumas dificuldades porque existe uma chamada a um método estático (podemos usar JMockit, porém, dependendo do retorno, torna-se uma tarefa muito complicada) e um instanciamento da ClasseUtil.

Como poderíamos evitar isso? Bem simples:

Passaremos a instância da ClasseUtil como parâmetro do método:
0: public class ClasseA {
1: public void metodo1(){
2: ClasseB classeB = new ClasseB();
3: classeB.metodo2(new ClasseUtil());
4: }
5: }

0: public class ClasseB {
1: public void metodo2(ClasseUtil classeUtil){
2: System.out.println(classeUtil.m1());
3: System.out.println(classeUtil.m2());
4: }
5: }

O design fica bem simples e os testes mais fáceis de implementar.