web-dev-qa-db-ja.com

スイッチのeclemmaブランチのカバレッジ:19件中7件が失敗

私にはこのスイッチシステムがあり、eclemmaを使用してブランチカバレッジをテストしています。すべてに対してブランチカバレッジで少なくとも80%が必要なので、できる限りテストするようにしています。ただし、このスイッチシステムはブランチカバレッジに関して十分にテストされていないことがeclemmaからわかりました。

pos = p.getCurrentPosition().substring(0, 1);
switch (pos) {
            case "G":
                goalkeepers++;
                break;
            case "D":
                defense++;
                break;
            case "M":
                midfield++;
                break;
            case "F":
                offense++;
                break;
            case "S":
                substitutes++;
                break;
            case "R":
                reserves++;
                break;
        }

私はこれらの各ケースを通過するために簡単なJUnitテストを使用しました。それでもエクレマはこれを黄色としてマークし、「19のブランチのうち7つが失敗した」と言います。このスイッチシステムを通過する方法は7つしかありません(6つの個別のケース+すべて未定義)。

スタックオーバーフローで同様の質問を検索してみました。それらのいくつかは、完全なカバレッジのためにif/elseを使用するソリューションとしてありました。これがこのカバレッジを取得する唯一の方法であるかどうかはわかりません。

これらの19のブランチすべてがどこから来ているのか、このスイッチのケースで100%のブランチカバレッジを取得するためにこれらの7つのブランチをテストする方法を誰かが説明できますか?

26
Gaargod

Javaコンパイラは、switch-caseコードをtableswitchまたはlookupswitchに変換します。tableswitchは、異なるケースの間にはいくつかのギャップがあります。それ以外の場合、lookupswitchが使用されます。

あなたのケースではtableswitchが使用されますあなたのケースのハッシュコードは間隔が狭いためです(owaismによって参照されるコードとは異なります):

  16: tableswitch   { // 68 to 83
                68: 111 // 'D'
                69: 183
                70: 141 // 'F'
                71: 96  // 'G'
                72: 183
                73: 183
                74: 183
                75: 183
                76: 183
                77: 126 // 'M'
                78: 183
                79: 183
                80: 183
                81: 183
                82: 171 // 'R'
                83: 156 // 'S'
           default: 183
      }

コロンの左側の番号は、順序付けされたハッシュコードとそれらの間の埋められたギャップです。右側の番号は、ジャンプ先です。 (Javaでは、文字のハッシュコードはASCII値です。)

68は "D"(最低のもの)のハッシュコードであり、83は "S"(最高のもの)のハッシュコードです。 69は、実際のケース間のギャップの1つの値であり、デフォルトのケースにジャンプします。

ただし、EclEmmaはこれらの分岐をtableswitchのカバレッジ計算から除外すると想定しています(ギャップがあるため、カバレッジがさらに低下します)。 0(カウントされた)ブランチはまだあります

次に、文字列値の等価比較が各ジャンプ先で実行されます(デフォルトの場合を除く)。 switch-caseは6つのケースで構成されているため、等しい比較で6つの6つのジャンプ先があります。

ケース「G」の比較のバイトコードは次のとおりです。

  96: aload_3
  97: ldc           #10
  99: invokevirtual #11  Java/lang/Object;)Z
 102: ifeq          183
 105: iconst_0
 106: istore        4
 108: goto          183
 111: aload_3

EclEmmaは2つの分岐をカウントします。入力文字列とケース文字列が等しいか、等しくないかのいずれかです。したがって、比較のために6 * 2分岐があります(デフォルトのケースでは分岐しません)。

次に、2つの文字列が等しい場合、ケースのインデックスが格納されます(バイトコード行105-106( "G"の場合)。次に、2番目のtableswitchへのジャンプが実行されます。それ以外の場合、ジャンプは直接実行されます。

 185: tableswitch   { // 0 to 5
                 0: 224
                 1: 237
                 2: 250
                 3: 263
                 4: 276
                 5: 289
           default: 299
      }

このスイッチは、以前に格納されたケースインデックスで動作し、ケース内のコードにジャンプします(ケース "G"にはインデックス0、デフォルトのケースには-1)。 EclEmmaカウント7ブランチ(6ケースとデフォルトケース)

したがって、最初のtableswitchにはカウントされたブランチが0個あり、equalsの比較には12個のブランチがあり、2番目のtableswitchにはさらに7個のブランチがあります。全体として、これは19のブランチになります


あなたのテストは6つの等しくないブランチをカバーしていません。これらをカバーするには、各ケースの文字列を見つける必要があります。ケース条件と等しくないが、同じハッシュコードを持っている。それは可能ですが、明らかに賢明ではありません...

おそらく、EclEmmaの分岐カウントは将来調整されるでしょう。

さらに、どのケースにも一致しないテストケースがないと思います(したがって、(暗黙)デフォルトのケースは対象外です)

32
nrainer

次のリンクを確認してください: http://sourceforge.net/p/eclemma/discussion/614869/thread/80e770df/

以下は、上記のリンクからの抜粋です。

これは、3つのケースを持つスイッチの例です。

これは非常に興味深い観察です。バイトコードを見ると、Javaコンパイラが文字列の切り替えをどのように処理するかがわかります。実際には3つのステップのプロセスです。

  1. ハッシュコードをオンにします(3つのブランチ、1つのデフォルト)
  2. 各ハッシュコードに対して等号(3 * 2ブランチ)を実行します
  3. ケースを実際に実行するための最後の切り替えを行います(3つのブランチ、1つのデフォルト)

したがって、合計14のブランチがあり、ソースコードの観点からは奇妙に見えます。さらに奇妙に見えるのは、3つ欠けていることです。説明は、ハッシュコードの後に​​、equalsメソッドが追加で適用されるステップ2です。これらのブランチをカバーするには、同じハッシュコードを持つ他の文字列を見つける必要があります。これは間違いなく、JaCoCoの将来のバージョンでカバレッジレポートから除外される可能性があるものです。
https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions

0
owaism