假设有五个类:C0, C1, C2, C3, C4,它们都实现了接口I:
public interface I {
public void run();
}
C0根据业务要求会调用C1, C2, C3, C4的run()方法(这四个类的run()方法中都会进行数据库操作):
public class C0 implements I {
public void run() {
I c1 = new C1();
c1.run();
//...
//执行若干操作
//...
I c2 = new C2();
c2.run();
//...
//执行若干操作
//...
I c3 = new C3();
c3.run();
//...
//执行若干操作
//...
I c4 = new C4();
c4.run();
//...
//执行若干操作
//...
}
}
在对C0的run()方法进行单元测试时发现了一个问题,C1~C4会进行数据库操作,比较费时,而且对它们的初始化都硬编码在C0的run()方法中了,所以编写C0的测试用例时无法用MockData来模拟C1~C4。
现在想到一个办法就是把C0改成下面这个样子:
public class C0 implements I {
private I c1;
private I c2;
private I c3;
private I c4;
public C0(I argC1, I argC2, I argC3, I argC4) {
c1 = argC1;
c2 = argC2;
c3 = argC3;
c4 = argC4;
}
public void run() {
c1.run();
//...
//执行若干操作
//...
c2.run();
//...
//执行若干操作
//...
c3.run();
//...
//执行若干操作
//...
c4.run();
//...
//执行若干操作
//...
}
public I getC1() {
return c1;
}
public void setC1(I argC1) {
c1 = argC1;
}
//...
//此处省略了c2~c4对应的getter/setter
//...
}
这样的话,编写C0的测试用例时就可以使用jMock或EasyMock一类的工具来模拟C1~C4了。但如果还有C5, C6甚至是C10呢?那么在C0里可能会满是getter/setter,而且C0的构造器的参数列表也过长了。
本质上,我觉得这是个OOP的问题,也就是一个类该怎样设计,才便于对它进行隔离的单元测试,但又不致于让这个类变得很丑陋?很想看看各位有何高见?