Покрытие кода Jacoco: несуществующий статический блок показывает только 75% покрытия

#java #unit-testing #code-coverage #jacoco

#java #модульное тестирование #покрытие кода #jacoco

Вопрос:

У меня есть два статических метода в классе BrickSortParallel. Они полностью охвачены модульными тестами. Но у меня есть статический блок static {...} , указанный Jacoco только с 75% покрытием кода. Что это означает?

Снимок экрана покрытия

 
public static int computeOddTaskCount(int length) {
    if (length < 0) throw new IllegalArgumentException("Illegal argument value: "   length);
    return isOdd(length) ? length >> 1 : abs(length - 1) >> 1;
  }

  public static int computeEvenTaskCount(int length) {
    if (length < 0) throw new IllegalArgumentException("Illegal argument value: "   length);
    return length >> 1;
  }
  

Ниже приведены тестовые примеры для обеспечения полного покрытия кода для вышеуказанных методов:

 class ComputeTaskCountTest {

private static final String ZERO_TASKS_EXPECTED = "Zero tasks expected.";
  private static final String ONE_TASK_EXPECTED = "One task expected.";
  private static final String HALF_TASKS_EXPECTED = "Half tasks expected.";
  private static final String ILLEGAL_LENGTH_EXPECTED = "Illegal length expected.";

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testZeroLength")
    void testZeroLength() {
      assertEquals(0, computeOddTaskCount(0), ZERO_TASKS_EXPECTED);
      assertEquals(0, computeEvenTaskCount(0), ZERO_TASKS_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testMinusOneLength")
    void testMinusOneLength() {
      assertThrows(
          IllegalArgumentException.class, () -> computeOddTaskCount(-1), ILLEGAL_LENGTH_EXPECTED);
      assertThrows(
          IllegalArgumentException.class, () -> computeEvenTaskCount(-1), ILLEGAL_LENGTH_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testMinusTwoLength")
    void testMinusTwoLength() {
      assertThrows(
          IllegalArgumentException.class, () -> computeOddTaskCount(-2), ILLEGAL_LENGTH_EXPECTED);
      assertThrows(
          IllegalArgumentException.class, () -> computeEvenTaskCount(-2), ILLEGAL_LENGTH_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testMinValueLength")
    void testMinValueLength() {
      assertThrows(
          IllegalArgumentException.class,
          () -> computeOddTaskCount(Integer.MIN_VALUE),
          ILLEGAL_LENGTH_EXPECTED);
      assertThrows(
          IllegalArgumentException.class,
          () -> computeEvenTaskCount(Integer.MIN_VALUE),
          ILLEGAL_LENGTH_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testOneValueLength")
    void testOneValueLength() {
      assertEquals(0, computeOddTaskCount(1), ZERO_TASKS_EXPECTED);
      assertEquals(0, computeEvenTaskCount(1), ZERO_TASKS_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testTwoValueLength")
    void testTwoValueLength() {
      assertEquals(0, computeOddTaskCount(2), ZERO_TASKS_EXPECTED);
      assertEquals(1, computeEvenTaskCount(2), ONE_TASK_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testThreeValueLength")
    void testThreeValueLength() {
      assertEquals(1, computeOddTaskCount(3), ONE_TASK_EXPECTED);
      assertEquals(1, computeEvenTaskCount(3), ONE_TASK_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testFourValueLength")
    void testFourValueLength() {
      assertEquals(1, computeOddTaskCount(4), ONE_TASK_EXPECTED);
      assertEquals(2, computeEvenTaskCount(4), "Two tasks expected");
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testMaxValueLength")
    void testMaxValueLength() {
      assertEquals(
          Integer.MAX_VALUE / 2, computeOddTaskCount(Integer.MAX_VALUE), HALF_TASKS_EXPECTED);
      assertEquals(
          Integer.MAX_VALUE / 2, computeEvenTaskCount(Integer.MAX_VALUE), HALF_TASKS_EXPECTED);
    }

    @Test
    @DisplayName("BrickSortParallelTest.ComputeTaskCountTest.testMaxValueLengthEven")
    void testMaxValueLengthEven() {
      assertEquals(
          (Integer.MAX_VALUE - 2) / 2,
          computeOddTaskCount(Integer.MAX_VALUE - 1),
          HALF_TASKS_EXPECTED);
      assertEquals(
          (Integer.MAX_VALUE - 1) / 2,
          computeEvenTaskCount(Integer.MAX_VALUE - 1),
          HALF_TASKS_EXPECTED);
    }
  }

  

Я что-то пропустил в вышеупомянутом тестовом классе?

Полные классы доступны по адресу:

https://raw.githubusercontent.com/Fernal73/DSAlgos/master/src/test/java/ds/tests/BrickSortParallelTest.java

https://raw.githubusercontent.com/Fernal73/DSAlgos/master/src/main/java/ds/BrickSortParallel.java

 $ java --version
openjdk 11.0.8 2020-07-14
OpenJDK Runtime Environment (build 11.0.8 10)
OpenJDK 64-Bit Server VM (build 11.0.8 10, mixed mode)
  

Версия Jacoco: 0.85

Из отчета следует, что статический блок имеет цикломатическую сложность 2 и покрыт только одной ветвью, что подразумевает, что для покрытия этой возможности не хватает тестового примера.

Комментарии:

1. Разве это не говорит вам, какие ветви были покрыты, а какие нет?

2. Нет, просто статическое {…} покрытие 75% в HTML-отчете, сгенерированном Maven. Когда я нажимаю на него, это приводит меня к первой строке определения класса.

3. Запуск вашего модульного теста в IntelliJ (с JaCoCo coverage runner) отображает 100%-ное покрытие тестов для методов, которые вы указали выше ( см. Изображение ). Может быть, попробуйте mvn clean очистить выходной каталог вашего проекта и повторно запустить тесты?

4. Хм … я также ожидаю полного покрытия. Вот почему я удивлен.

5. @Thomas Должен ли я использовать что-либо другое, кроме w3m, из командной строки для просмотра отчета?

Ответ №1:

Цитирую Марка Р. Хоффмана на Jacoco Github:

«В вашем коде используется ключевое слово assert, которое приводит к статическому инициализатору:

 static {};
  descriptor: ()V
  flags: (0x0008) ACC_STATIC
  Code:
    stack=1, locals=0, args_size=0
       0: ldc           #72                 // class ds/BrickSortParallel
       2: invokevirtual #73                 // Method java/lang/Class.desiredAssertionStatus:()Z
       5: ifne          12
       8: iconst_1
       9: goto          13
      12: iconst_0
      13: putstatic     #31                 // Field $assertionsDisabled:Z
      16: return
    LineNumberTable:
      line 19: 0
    StackMapTable: number_of_entries = 2
      frame_type = 12 /* same */
      frame_type = 64 /* same_locals_1_stack_item */
        stack = [ int ]
  

Это известное ограничение. Обходной путь заключается в том, чтобы не использовать assert. «

Следующий фрагмент кода разрешает головоломку:

 
  @Generated
private void assertEquality(int  size, int count) {
if (size != count)
 throw new AssertionError("Size is not the same as count.");
    }
  

Он выдает ошибку утверждения, и все же @Generated аннотация удовлетворяет требованиям к покрытию кода. Однако это обходной путь. Решение состоит в том, чтобы дождаться, пока Jacoco предоставит необходимую фильтрацию утверждений в байтовом коде.

Выполняется фильтрация утверждений.