[b]100%的测试覆盖率[/b]
这是一个很显而易见的答案。但是我不认为这是正确的答案。下面是一个小例子:
[code]
private int[] map = new int[] {
1, 3, 5, 8};
public int oddNumber(int index) {
return map[index - 1];
}
[/code]
写一个简单的测试
[code]
@Test
public void first_odd_number_should_be_one() {
assertEquals(1, oddNumber(1));
}
[/code]
这个测试是不是覆盖了100%的代码呢?我认为是覆盖了的。但是是不是真的测试了所有的执行路径呢?显然没有。oddNumber(4)应该是7,但是这个程序会返回8。我们改一个写法:
[code]
public int oddNumber(int index) {
switch(index) {
case 1: return 1;
case 2:return 3;
case 3: return 5;
case 4: return 8;
}
throw new UnsupportedOperationException();
}
[/code]
换了一种写法之后,测试覆盖率立马就下去了。差别就在于,在第二种写法把执行路径对应到了字面的静态路径上了。所以说,测试应该覆盖是否完全的标准,应该是以动态路径为准,而不是以静态路径为准。同时,也提醒我们,想要靠最后根据测试覆盖率来补测试,是不能让你做到TDD时同等程度的自信的。
在TWU上学的时候,老师给了一个简单的准则:
[b]代码增一行太多,减一行太少[/b]
当然,老师是用英文教课的,我是把意思用中文翻译了一下。这句话是说,代码不能多不能少,以恰好让所有测试通过为最佳。为了验证这个道理,据老师说,[b]Thought[/b]Works UK有一个开发人员写了一个工具用来“删代码”。如果有一行代码是能够删掉之后还能让所有测试都通过的,那么就删掉它。我觉得,这个准则很有务实,也很有用。同时,也回答了另外一个问题,[b]是不是要给所有的类写对应的测试类来覆盖其所有的行为[/b]?我认为没有根据这个准则,答案应该是没有必要。只要你的测试覆盖了这个类的所有执行路径就可以了。至于这个测试是不是针对这个类独立进行测试的,是单元测试还是功能测试,是黑盒测试还是白盒测试,都不重要。重要的是,你删掉这个类中一行或者几行,都会让所有测试中至少一个测试失败。这就能说明,你的测试是写到位了。
当然,再加一些怀疑和批评的态度。对于上面那个放之四海皆准的准则,有一个例外:那就是这个准则只关心程序的正确性,但是我要说程序的行为是由正确性和效率共同组成的。举一个空泛的例子:
[code]
if (a) {
doSomethingUnderConditionA();
} else if (b) {
doSomethingUnderConditionB();
}
[/code]
条件A和B可能在匹配的集合上是包含关系,也就是说一个是另外一个的强条件。假设A是更强的条件,那么在条件更强的情况下,我们往往可以给一个更有效率的解。但是有可能,如果没有条件A这个分支,所有情况都走条件B的分支,解仍然是正确的。所有说,A的存在不影响程序的正确性,只影响了程序在特定条件下的效率。那么,在这种情况下,就不能说代码增一行太多了。
这是一个很显而易见的答案。但是我不认为这是正确的答案。下面是一个小例子:
[code]
private int[] map = new int[] {
1, 3, 5, 8};
public int oddNumber(int index) {
return map[index - 1];
}
[/code]
写一个简单的测试
[code]
@Test
public void first_odd_number_should_be_one() {
assertEquals(1, oddNumber(1));
}
[/code]
这个测试是不是覆盖了100%的代码呢?我认为是覆盖了的。但是是不是真的测试了所有的执行路径呢?显然没有。oddNumber(4)应该是7,但是这个程序会返回8。我们改一个写法:
[code]
public int oddNumber(int index) {
switch(index) {
case 1: return 1;
case 2:return 3;
case 3: return 5;
case 4: return 8;
}
throw new UnsupportedOperationException();
}
[/code]
换了一种写法之后,测试覆盖率立马就下去了。差别就在于,在第二种写法把执行路径对应到了字面的静态路径上了。所以说,测试应该覆盖是否完全的标准,应该是以动态路径为准,而不是以静态路径为准。同时,也提醒我们,想要靠最后根据测试覆盖率来补测试,是不能让你做到TDD时同等程度的自信的。
在TWU上学的时候,老师给了一个简单的准则:
[b]代码增一行太多,减一行太少[/b]
当然,老师是用英文教课的,我是把意思用中文翻译了一下。这句话是说,代码不能多不能少,以恰好让所有测试通过为最佳。为了验证这个道理,据老师说,[b]Thought[/b]Works UK有一个开发人员写了一个工具用来“删代码”。如果有一行代码是能够删掉之后还能让所有测试都通过的,那么就删掉它。我觉得,这个准则很有务实,也很有用。同时,也回答了另外一个问题,[b]是不是要给所有的类写对应的测试类来覆盖其所有的行为[/b]?我认为没有根据这个准则,答案应该是没有必要。只要你的测试覆盖了这个类的所有执行路径就可以了。至于这个测试是不是针对这个类独立进行测试的,是单元测试还是功能测试,是黑盒测试还是白盒测试,都不重要。重要的是,你删掉这个类中一行或者几行,都会让所有测试中至少一个测试失败。这就能说明,你的测试是写到位了。
当然,再加一些怀疑和批评的态度。对于上面那个放之四海皆准的准则,有一个例外:那就是这个准则只关心程序的正确性,但是我要说程序的行为是由正确性和效率共同组成的。举一个空泛的例子:
[code]
if (a) {
doSomethingUnderConditionA();
} else if (b) {
doSomethingUnderConditionB();
}
[/code]
条件A和B可能在匹配的集合上是包含关系,也就是说一个是另外一个的强条件。假设A是更强的条件,那么在条件更强的情况下,我们往往可以给一个更有效率的解。但是有可能,如果没有条件A这个分支,所有情况都走条件B的分支,解仍然是正确的。所有说,A的存在不影响程序的正确性,只影响了程序在特定条件下的效率。那么,在这种情况下,就不能说代码增一行太多了。