Покрытие ветви коммутатора для коммутатора: 7 из 19 пропущено

У меня есть эта система коммутации, и я использую 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 для прохождения каждого из этих случаев. Тем не менее, эклемма помечает это как желтый и говорит, что "7 из 19 ветвей пропущены". Я бы сказал, что есть только 7 способов пройти через эту систему переключения (6 отдельных случаев + все неопределенные).

Я попытался найти похожие вопросы по переполнению стека. Некоторые из них имели в качестве решения использовать if/else для полного охвата. Я не уверен, что это единственный способ получить это покрытие.

Кто-нибудь может объяснить, откуда берутся все эти 19 веток и как я могу протестировать эти оставшиеся 7, чтобы получить 100% покрытие ветвей в этом случае коммутатора?

2 ответа

Решение

Компилятор Java переводит код регистра переключателя в 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 является значением одного из промежутков между реальными случаями и будет переходить к случаю по умолчанию.

Однако я предполагаю, что EclEmma исключает эти ветви из вычисления покрытия tableswitch (это могло бы снизить охват еще больше из-за пробелов). Итак, у нас еще 0 (подсчитанных) веток.

Затем выполняется сравнение с равным значением строки в каждом месте назначения перехода (кроме одного случая по умолчанию). Поскольку ваш коммутатор состоит из 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 считает две ветви: либо входная строка и строка регистра равны, либо нет. Таким образом, у нас есть 6 * 2 веток для сравнения. (Случай по умолчанию не разветвляется.)

Далее, если две строки равны, индекс регистра будет сохранен (строки байтового кода 105-106 для случая "G"). Потом прыжок ко второму 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 случаев плюс случай по умолчанию).

Следовательно, у нас 0 подсчитанных ветвей в первом tableswitch 12 филиалов в equals сравнения и еще 7 веток во втором tableswitch, В целом, это приводит к 19 филиалам.


Ваши тесты не охватывают ни одну из 6 не равных ветвей. Чтобы покрыть их, вам нужно будет найти строку для каждого случая, которая не равна условию случая, но имеет тот же хэш-код. Это возможно, но определенно не разумно...

Возможно, подсчет веток EclEmma будет скорректирован в будущем.

Более того, я полагаю, что у вас нет тестового примера, который не соответствует ни одному из случаев (таким образом, (неявный) случай по умолчанию не рассматривается.)

Проверьте следующую ссылку: http://sourceforge.net/p/eclemma/discussion/614869/thread/80e770df/

Ниже приведен фрагмент вышеуказанной ссылки:

Это для примера, имеющего переключатель с 3 случаями:

Это довольно интересное наблюдение. Из байт-кода видно, как компилятор Java обрабатывает переключение в строках. На самом деле это трехэтапный процесс:

  1. Включите хэш-код (3 ветви, 1 по умолчанию)
  2. Для каждого хеш-кода делайте равные (3 * 2 ветви)
  3. Выполните окончательное переключение для фактического выполнения дел (3 ветви, 1 по умолчанию)

Таким образом, у нас есть всего 14 ветвей, что выглядит странно с точки зрения исходного кода. Что выглядит еще более странным, так это то, что вам не хватает трех из них. Объяснение - это шаг 2, где метод equals применяется дополнительно после хеш-кода. Чтобы покрыть эти ветви, вам также нужно найти другие строки с таким же хеш-кодом. Это определенно то, что может быть отфильтровано из отчетов о покрытиях в будущих версиях JaCoCo:
https://sourceforge.net/apps/trac/eclemma/wiki/FilteringOptions

Другие вопросы по тегам