关于单元测试的内容很多,关于可维护性的内容也很多,在这里主要关注单元测试代码可维护性中的两点。
重复
条件测试
重复
重复:同一概念,多处表达。从形式可以把重复分类为:代码内容完全一样,代码结构相同,结构不同但语义相近。
获取重复的感性认识
代码完全相同
在多个文件中到处充斥着
log("当前网络不可达!");
在比如, 下面的两处空字符串""完全一样,两处 "plainText"完全一样.
public class TemplateTest {
@Test
public void emptyTemplate() throws Exception {
assertEquals("", new Template("").toString());
}
@Test
public void plainTextTemplate() throws Exception {
assertEquals("plainText", new Template("plainText").toString());
}
}
代码结构相同
比如下面的代码,结构是相同的
public class TemplateTest {
@Test
public void emptyTemplate() throws Exception {
String template = "";
assertEquals(template, new Template(template).toString());
}
@Test
public void plainTextTemplate() throws Exception {
String template = "plainText",;
assertEquals(template, new Template(template).toString());
}
}
<a name="semanteme_repeat"></a>结构不同但语义相近
下面两处内容、结构都不同相同。但可以抽象为通过某种条件对一个集合进行过滤,最后得到目标集合。
@Test
public void groupShouldContainTwoSupervisors() {
List<Employee> all = group.list();
List<Employee> all = new ArrayList<>(all);
Iterator<Employee> i = employees.iterator();
while(i.hasNext()) {
Employee employee = i.next();
if (!employee.isSupervisor()) {
i.remove();
}
}
assertEquals(2, employee.size());
}
@Test
public void groupShouldContainFiveNewcomers() {
List<Employee> newcomers = new ArrayList<>();
for (Employee employee : group.list()) {
DateTime oneYearAgo = DateTime.now().minusYears(1);
if (employee.startingDate().isAfter(oneYearAgo)) {
newcomers.add(employee);
}
}
assertEquals(5, newcomers.size());
}
去重后
@Test
public void groupShouldContainTwoSupervisors() {
groupShouldContains(2, group.list(), employeeFilter);
}
@Test
public void groupShouldContainFiveNewcomers() {
groupShouldContains(2, group.list, customFilter);
}
抽象接口
public interface Filter<T> {
List<T> filter(List<T> objects);
}
抽取公共方法
private void groupShouldContains(int expectedCount, List group, Filter filter) {
List list = filter.filter(group);
assertEquals(expectedCount, list.size());
}
怎么去掉重复
根据重复的概念---“同一个概念,在多处表达” 所以我们要第一步先抽象出表达的概念。第二步 把所有出现重复的地方用我们抽象的概念形成一个统一的命题。
第一步 抽象概念
我要打印一个树,可以这样实现,printTree 等价于11条print语句
print " * "
print " *** "
print " ***** "
print "*******"
print " * "
print " *** "
print " ***** "
print "*******"
print " # "
print " # "
print " # "
同样的问题还可以这样看:printTree等价于先打印两个树冠,在打印一个树干。
printCrown();
printCrown();
printTreeTrunk();
这里的树冠(crown)和树干(trunk)就是我们抽象出来的概念。
在上面语义重复的例子 抽象出的概念是过滤器的概念。
第二步 统一命题
在上面语义重复的例子 抽象出过滤器的概念后,可以形成这样一个统一的命题:如果一个原始集合经过过滤器过滤,那么会生成一个符合预期的新集合。
有了过滤器的概念不难定义一个过滤器对象,然后把命题用过滤器对象翻译出来。
从可读性的角度看重复
我们知道可读性是可维护性的前提,那么重复的代码是怎么影响可读性的呢?
首先要说一下可读性的概念。什么是可读性?可读性可以定义为理解一段代码的意图所需要的时间。时间越短可读性越好。
而重复会造成我们看到同一个概念还要继续思考一下才明白这是同一个概念。如果重复的形式是内容完全相同那么还好一下,如果是结构重复,甚至是语义重复,那么我们理解代码段的意图耗费的时间就更长了,如下图所示。
所以不管是在产品代码还是测试代码中我们都要尽最大努力去掉重复。
条件测试
待续...