Menu

FrontPage

Eclipseプラグイン
Eclipse小技集
リファクタリング講座
テスティングフレームワーク JUnit
MSDEについて



The 20 newest affair
2010-09-092010-09-082010-09-072010-09-062010-09-012010-08-312010-08-272010-08-242010-08-232010-08-172010-08-162010-07-302010-07-212010-07-132010-06-272010-06-182010-06-172010-06-16

デッド・コードのCoverageReport


テストの準備


以下のjavaプログラムをdJUnitを使用してテストしてみます。
  
01 public class HelloWorld {
02
03  public HelloWorld() {
04  }
05  
06  public static void main(String args[]) {
07
08    if (false) {
09       System.out.println("don't never pass...");
10    }
11
12    System.out.println("HelloWorld.");
13  }
14 }
      


対するテストケースはHelloWorldのテストで使用したテストケースを使用します。
(デフォルトコンストラクタのCoverageを通すためのコードを追加してます。→18行目)
  
01 import jp.co.dgic.testing.framework.DJUnitTestCase;
02
03 public class HelloWorldTest extends DJUnitTestCase {
04
05  public HelloWorldTest(String name) {
06    super(name);
07  }
08
09  protected void setUp() throws Exception {
10    super.setUp();
11  }
12
13  protected void tearDown() throws Exception {
14    super.tearDown();
15  }
16
17  public void testMain() {
18    new HelloWorld();
19    HelloWorld.main(null);
20
21    String arg = (String) getArgument("java.io.PrintStream", "println", 0);
22    assertEquals("HelloWorld.", arg);
23  }
24 }
      


このテストを実行するにあたって、以下のことが予想できます。

  1. テストは通るはずである。
  2. CoverageReportは100%にはならないはずである。

1はHelloWorldのテストで検証済みなので問題ないと思います。
2は、プログラム内の
  
1  if (false) {
2    System.out.println("don't never pass...");
3  }
      


という部分が実行されず、CoverageReportでは未実行とされるはずです。


テスト結果


以下の画像はこのテストを実行して生成されたCoverageReportです。

[66616C7365312E706E67.png]


バッチリ100%になりました。。。


これは、JCoverageにも言えることなのですが、javaコンパイラによる最適化が影響しているようです。

今回のような

 if(false)

という絶対に通らない部分(デッド・コード)を含むソースをコンパイルすると、コンパイラがデッド・コード除去という最適化を行い、
決して実行されないブロックのバイトコードを生成しないようにするらしいのです。

ということは、コンパイル後にはもうこの分岐は存在せず、単に「HelloWorldを出力するプログラム」になってしまうのです。


そこで、ある実験をしてみます。

まず、今回のテストによるCoverageReportのlinesは行となっていることを確認しておいて下さい。

[66616C7365322E706E67.png]


ちょこっとソースを変更してまた同じテストを実行します。

以下は変更後のテスト対象ソースコードです。
 
01 public class HelloWorldForDeadCode {
02
03  public HelloWorldForDeadCode() {
04  }
05  
06  public static void main(String args[]) {
07
08    if (false) {
09       System.out.println("don't never pass...");
10       System.out.println("don't never pass...");
11       System.out.println("don't never pass...");
12       System.out.println("don't never pass...");
13       System.out.println("don't never pass...");
14       System.out.println("don't never pass...");
15       System.out.println("don't never pass...");
16       System.out.println("don't never pass...");
17       System.out.println("don't never pass...");
18       System.out.println("don't never pass...");
19       System.out.println("don't never pass...");
20       System.out.println("don't never pass...");
21       System.out.println("don't never pass...");
22       System.out.println("don't never pass...");
23       System.out.println("don't never pass...");
24       System.out.println("don't never pass...");
25       System.out.println("don't never pass...");
26       System.out.println("don't never pass...");
27       System.out.println("don't never pass...");
28       System.out.println("don't never pass...");
29    }
30
31    System.out.println("HelloWorld.");
32  }
33 }
      


デッド・コード対象のif文の行数を増やしてみました。
もしデッド・コードのバイトコードが生成されていないのならば、このテストのCoverageReportのlinesもになるはずです。

そしてこれがそのCoverageReportです。

[66616C7365332E706E67.png]



デッド・コードの除去は

コンパイル時に式の値が false になる場合

に動作するようです。

例)

 if (false)                   ・・・あきらかにfalseの場合

   → デッド・コード対象です。

 if (1 + 1 == 0)             ・・・計算式の結果が必ずfalseの場合

   → デッド・コード対象です。

 if (callReturnFalseMethod())      ・・・実行時に式の値がfalseになる場合

   → デッド・コード対象外(バイトコードが生成される)です。


JCoverage(CoverageReport)は、コンパイルされたclassファイルに対して行番号を与えてテストの実行・未実行を検証するので、このような現象が起こってしまったと考えられます。



参考:http://www-6.ibm.com/jp/developerworks/java/000818/j_pr29.html


Attached File: [Attached File All List] false1.png[81KB] false2.png[9KB] false3.png[11KB]

Lastmodified: 2005-05-12 (木) 18:40:41 (1945d)