web-dev-qa-db-ja.com

assertNullの代わりにassertTrueを使用するとブランチが欠落する

Java/Junitでは、いくつかのオブジェクトを使用してnullをテストする必要があります。条件をテストするにはさまざまな方法がありますが、ほとんどのテストでassertTrueを使用しています。 assertTrueでnullをチェックすると、EclEmmaは1つのブランチのみをテストしていると述べています。

ステートメントを手動で変数に解決すると(結果をブール値に設定してassertTrueに渡すなど)、コードカバレッジはアサートでは完了したと見なされますが、変数の初期化行では完了していないと見なされます。

なんでこんなことが起こっているの?これは、Javaが http://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions で述べたように明らかに追加する追加のバイトコードに関連していますか?ソリューション(他のassertステートメントを使用する以外)。

assertTrue:

assertTrue( myObject == null ); //1 of 2 branches

assertTrue:

boolean test = (myObject == null); //1 of 2 branches missing
assertTrue(test); // complete

assertNull:

assertNull( myObject ) //complete;
17

ほとんどのブール式では、Javaコンパイラはバイトコードに追加のブランチを生成します。 JaCoCoは、元のJavaコードではなく、生成されたバイトコードに基づいて「ブランチカバレッジ」を生成するため、使用するほとんどすべてのブール式の追加のブランチカバレッジ情報を表示します。

コードでは、使用するブール式はmyObject == nullです。

この値を計算するために、Javaコンパイラは、スタック上の2つの引数をプッシュするコードを生成し、スタック上で1(true)または0(false)をプッシュするために条件付きジャンプを実行します。 JaCoCoは、この条件付きジャンプのブランチカバレッジを報告します。

したがって、myObject == nullを使用するという事実は、説明する動作をトリガーします。

他のいくつかの例として、これを試してください:

boolean t = true;
boolean f = false;
boolean result1 = (t && f) || f; // 3 out of 6 missed.
boolean result2 = !t;            // 1 out of 2 missed.

これは、たとえば、ブール式が関数によって返される場合に役立ちます。関数は、別の場所でif-then-elseステートメントの条件として使用されます。ほとんどの場合、Javaコンパイラの動作の結果ですが、元のJavaコードの条件カバレッジ(単なるブランチカバレッジではなく)を評価するのに役立ちます。

この機能はあまり文書化されていませんが、ここにいくつかの指針があります。

したがって、実際には、生成される追加のバイトコードに関連していますが、フィルタリングオプションの対象となる合成バイトコンパイラ構造の特定の例には関連していません。

注:最初の回答は推測が多すぎたため、主要な編集を行いました。良い批判的な議論をしてくれた@ ira-baxterに感謝します。

22
avandeursen

Emmaが条件式をIMHOをカウントする(ブランチ)カバレッジの「ブランチのあるもの」として扱うという事実は、単純に壊れているように見えます。条件分岐ではありません。

アサーションについてもっと議論することができます。 「アサートの失敗時に例外をスローする」と定義されている場合、実際には条件分岐があります。 [私が思うように、私はJavaエキスパート]ではない]「アサートの失敗でプログラムを終了する」と定義されている場合、それは実際にはブランチではありません。メソッドもあいまいです。呼び出し;これらの条件分岐であり、呼び出されたメソッドが例外をスローした場合、制御フローは「ステートメントの残りの部分」に進みません。

Java Test Coverage ツールは、そのような条件付きの(ブランチ)カバレッジ分析を「正しく」取得します。

1
Ira Baxter

ブールメソッドで100%のコードカバレッジを取得するには、次の手順を実行します

Class RecordService{


    public boolean doesRecordExist(String id){

    return id!=null;

    }


    }

    //Method inside your mock
    @Test
    public boolean testDoesRecordExist(){
    RecordService recordService = mock(RecordService.class);
    when(recordService.doesRecordExists()).thenReturn(
                    anyString()).thenReturn(null);

    }
0
sunshine