代码覆盖的15种典型情景-1

上一篇 / 下一篇  2012-10-26 14:17:07 / 个人分类:杂谈

51Testing软件测试网S&O?]2HQS3Z A

  代码覆盖(Code Coverage)为何物?相信程序员特别是测试人 员不陌生,很多人都喜欢用代码覆盖来驱动测试的开展和完善。确实代码覆盖可以找出测试疏漏和代码问题,但是单纯的代码覆盖率高低并不能直接反映代码质量的 好坏。大多我们的努力方向都是找出那些没有覆盖到的代码,然后补充用例,完善测试。而摆在我们面前的问题是:是否我们已经充分认识到哪些不需要、不能、必 须被覆盖?只有对代码覆盖的各种情景了然于胸,才能不盲目乐观于代码覆盖率之高,悲观于代码覆盖率之低。在实践中(本文面向主要Java语言,基于 emma工具),梳理可知,对于代码覆盖我们可能都会遇到以下15种典型情景:

I'f [3pi sq/y051Testing软件测试网+@D&[}:f aY8t_ K:i.p

  1、代码覆盖

5Jl@7T,m8w/Ub0@5e)x0

*wt4@9xI0  即代码所有路径被经过,这种需要注意的是:不应该覆盖而被覆盖的情况。例如某种特殊异常就是不期望遇到的,但是遇到了,异常处理的代码也覆盖了,这时,我们应该追溯异常产生的根本原因,而不因覆盖了就直接忽略。

4\p;IE u051Testing软件测试网 L%h9VEE-v

  提示:不仅要关注未覆盖的代码,也要关注覆盖的,特别是偶然覆盖的代码。51Testing软件测试网 `m4i/S ~-zl Mkz;X

6Ug r y+hT-Wn @0  2、废弃的功能51Testing软件测试网W"dMHkv/}p&T

51Testing软件测试网n/SI"K)QTXo

  一些功能点随着产品版本不断更新,可能会被取消,这部分功能可以直接移除,保留只会让代码看起来越冗余。如果某天需要参考或找回删除的那些代码,CVS/SVN工具就搞定了。

j P,E aKBr"B0

4J;}Mo.]E4A0  提示:不用的功能不需要覆盖,要及时删除,不通过保留或者注释的方式残留在代码中。51Testing软件测试网yIL9w7J

51Testing软件测试网 w{-g!t0C%GX c

  3、工具类(助手类)、常量类等的私有构造器

5^uA @ B6z-bT0

d vmhh3{[0  工具类和常量类共有的特征是对外开放的都是静态方法,调用方法的时候,无需创建实例,所以推荐实践是创建一个private的构造器方法。这导致类的构造器代码无法覆盖(不考虑反射等方式)。51Testing软件测试网_$i ]f/T3m0l;qhp

+O(I-sd/?C8W0  相反,如果某天发现对于这样的类覆盖率为100%,那检查下是否代码写的不规范:用默认构造器,然后通过实例来调用静态方法。

0uS K*aIt?E051Testing软件测试网0v[G4rXb M.r

  例1:工具类51Testing软件测试网,c~"rQ ` K)?!VXW.?

8{3O/yk[6b \!}m0public final class StringUtil {

+l4_"?4]0]1OF7MZt0

;_Ju9@;U2Y'r0    public static String concatWithSpace(String... strings) {51Testing软件测试网*e \K"C;P"J F4x
        return concat(MarkConstants.SPACE, strings);
m9n$LR1V0k0    }

9O7of3hT0

7V-RuD\0
?7aJs,J9y,Jq0    public static String concatWithSemicolon(String... strings) {51Testing软件测试网6ii"r-^$s.Z mx X&m
        return concat(MarkConstants.SEMICOLON, strings);51Testing软件测试网-k!wN7i6t6L&SWu5|
    }

C/q0H d;h(B5D051Testing软件测试网G*WAwLk@

    private StringUtil() {51Testing软件测试网A:N;WvDT5IF^4oO
    }

|7@p{RY;X M0

[ C#gEpK&[0}

/?SG'FW0

4yP/U kL&WA0  例2:常量类51Testing软件测试网3[_ _Pb v+CiB

51Testing软件测试网2f5x3b5W `-hH

public final class MarkConstants {51Testing软件测试网8f5S a0_:b(Yg
    /**51Testing软件测试网6R.r#d(`&ZO4DgG;q E
     *{@value}
3i2[ybW5LY sj+H0     */
0HhmV}3A0    public static final String SEMICOLON = ";";
51Testing软件测试网-a'tEP1N+v;N

-R9j3[R wG$u1F0{ w0    private MarkConstants() {51Testing软件测试网J(z.jE"Fqd\ F Y
    }
51Testing软件测试网g\:Ao3U%C0V(z0_

[V8W5C;P.a4iN0}51Testing软件测试网&XFd2y#k"u

51Testing软件测试网\ ~,J+U5M*sl

  提示:工具类(助手类)、常量类等的私有构造器不能被覆盖

8f1n&U.w | I051Testing软件测试网2QzI#S9@G.H&T&I

  4、日志级别配置

h+}$z KGZ9ej7Iy051Testing软件测试网,E;]tr@;}8J#~

  日志级别不同,覆盖率高低也不同。在产品部署中,很少将日志的级别设成debug,因为日志占用磁盘空间会增长很快。只在做一些问题跟踪、调试时才会调高日志级别。51Testing软件测试网C8Xz7FQ ]s(R7q

51Testing软件测试网(YZ&GChF.T

  所以环境使用不同的日志级别,也会导致一些日志代码没有覆盖。如以下示例程序,不打开debug级别无法覆盖部分代码:

l$tg }(Q$N1pW(]^051Testing软件测试网6EgG+k0G5x;p ?

public static String formatPath(String path) {
7x r8{up]i)W0     ValidationUtil.checkString(path);
+P3n.Pc-e WA0     String returnPath = path.trim();
p|tv+r`v0     if (!returnPath.startsWith(SPLIT))
"i1l!C7Q;v6b(Z0         returnPath = SPLIT + returnPath;51Testing软件测试网!N'cq8W:I]n
     if (returnPath.endsWith(SPLIT))51Testing软件测试网|,Of%e"LHH
         returnPath = returnPath.substring(0, returnPath.length() - 1);

2`O TB9GS?'BO*{~051Testing软件测试网(y_Z X3_E6l9J

     if (LOGGER.isDebugEnabled())51Testing软件测试网aY'l%rh$\
         LOGGER.debug(String51Testing软件测试网J*FC3O{9N A
                 .format("[util]convert [%s] to [%s]", path, returnPath));
-EG9h)].O(^gGq0     return returnPath;51Testing软件测试网8Y1L Pq7^/|-d8U
}
51Testing软件测试网)|]"QK|R

*m'z"~he[\O#A%|~0  那么这部分代码需要覆盖嘛?需要。假设代码误写成:

@yt'Sw5PnZ`(g0

8^ j!xnc1r^4a0

z1e?%zX)@5?"I0
LOGGER.debug(String.format("[util]convert [%] to [%s]", path, returnPath));

b _g@`uf+\0  某天日志级别设为debug,就会发现报错。类似的还有日志中经常输出某个对象信息,但是该对象可能是null,从而抛出空指针异常。51Testing软件测试网 Ytj[fF^3u

^8Q9g#d/l+S@0  提示:日志也是代码的一部分,需要通过调整日志级别来覆盖。

$?NUv3xq `051Testing软件测试网 W [4cw ~X\ Us

  5、JVM等参数

(x$_ Z%}'t%H0

rE*{~}S0  程序的配置参数会直接影响代码路径覆盖,不仅包括业务上的一些配置,也包括依赖平台的参数,例如JVM参数除了会影响性能,也会影响代码的覆盖情况,例如断言相关参数:

iv7i@Bv7^1d0

2E O(iJb d7n0

8B+S-dH`(Orb5D S0
-ea[:...|:] 和-da[:...|:]
51Testing软件测试网3z \+Rd5@+tF;c

  分别是启用和关闭用户断言(-esa/-eda,针对系统断言),在JAVA中断言是默认关闭的,所以涉及断言的代码默认无法覆盖。

ym7L%q_0ts051Testing软件测试网7z;Y6p:r3s

  提示:一些代码路径能否覆盖与JVM等参数有关,需要通过调整参数来覆盖51Testing软件测试网.xGD z0X\ {:i

51Testing软件测试网*~P.t+gp!i)P-F

  6、main()方法

*[mB#H/W051Testing软件测试网g ol7h'fE#x7mO"\9C

  一些程序员喜欢临时写一个main()方法方便于测试,完成测试后寻思以后还能方便测试就留了下来。导致产品代码中的这些代码无法被覆盖。在产品代码中,应该删除这些,部署的毕竟是产品代码,不是测试代码。

v` x,A1pTG.hu&| o051Testing软件测试网I(B[[/@Vx] P

  提示:main()方不需要被覆盖,产品代码不保留测试代码51Testing软件测试网Z%_kE6hY/G

xE.]5_(V0  7、编码习惯写法

ECsv;XP051Testing软件测试网)Z7wJ w*N f?otJ2E

  在编码过程中,常常有一些习惯写法,最常见的比如:(1)覆盖toString()方法;(2)以意义配对形式写一些方法:比如数据连接中 Connect()搭配DisConnect(),枚举中常用的toString()搭配fromString(),这些惯用的写法告诉读者一些涵义,但 是不见得所有的方法都必须被调用,例如在产品应用中,我们可能启动起一个周期性的job,但是本身尚未添加“取消“功能(或本来就不需要停止)。自然也就 无法调用:但是它应该不应该存在?笔者认为作为完整的功能应该存在。类似的还有异常定义的时候,会定义很多重载的方法,虽然不见得每个都调用,但是不定某 天就会被调用。51Testing软件测试网Eq~at/g

51Testing软件测试网X]D4o(v*U&L

  提示:编码习惯写法造成的未覆盖代码需要被覆盖,是代码的一部分。

r;B%~t ?:n0

P9Dd{ b|[0  8、项目的使用方式

q9b5?$_k051Testing软件测试网l _:x]M"Q5aJ

  下面两种使用方式会造成代码不能全部被覆盖:

A"Y1L(V1[!fN051Testing软件测试网_U`Mc(o

  1)客户端Jar方式:部分代码作为客户端Jar包形式提供给他人使用;

3QvYQ{&R^0

n"Y,M8`+c3[.A0  2)分布式系统交互:分布式系统之间存在交互时,例如从系统1复制文件到系统2,如果始终按照从1到2的顺序,又仅仅统计系统1的代码覆盖肯定不能覆盖全部。需要覆盖的代码虽出于一处,但是使用方式不同也会导致在不合并覆盖数据情况下代码未覆盖。51Testing软件测试网V#Y?gR G

1u[k&v5?7WI/I0  提示:项目使用方式造成的代码覆盖统计数据分散需要通过合并数据来覆盖。

+E-si zp`051Testing软件测试网oY2g zASeDv

  9、常用最佳实践

s/rr d,Kz g0

H[^8s8E's0  一些很难覆盖的最佳实践:例如对于一些资源(IO,lock)的释放,可能直接try…catch然后记录异常,这些异常一般很难发生。51Testing软件测试网'c2zIa QItck

$l4fF%\ g!f6L?Ue0

N3REx$w5tj0
public static void close(InputStream inputStream)
7skG&^h)R+^A0{51Testing软件测试网1t^KSFx)quc1A D
         try {51Testing软件测试网s|Z5B/O
             inputStream.close();51Testing软件测试网p ci6s\&U/V
         } catch (Exception e) {
/F6}IIuN4f0             LOGGER.warn("fail to close inputstream");51Testing软件测试网3gkoT8\,?
         }
SG:n4?;t(M'o0}
51Testing软件测试网X.W'Z$\z~~"{T/K

  提示:常用最佳实践可以不覆盖。

%f|2Q$w0fi9m(HvZ8X0

TAG:

 

评分:0

我来说两句

Open Toolbar