8 веток для пробной работы с ресурсами - возможно ли покрытие jacoco?

У меня есть код, который использует попытку с ресурсами, а в jacoco он покрывается только наполовину. Все строки исходного кода зеленые, но я вижу маленький желтый символ, говорящий мне, что покрыты только 4 из 8 веток.

введите описание изображения здесь

Мне сложно понять, что это за ветки, и как написать код, который их покрывает. Три возможных места бросают PipelineException. Это createStageList(), processItem() и подразумеваемые close()

  1. Не бросая никаких исключений,
  2. выброс исключения из createStageList()
  3. выброс исключения из processItem()
  4. выброс исключения из close()
  5. выброс исключения из processItem() и close()

Я не могу придумать других случаев, но у меня все еще покрыто только 4 из 8.

Может кто-нибудь объяснить мне, почему это 4 из 8 и есть ли способ поразить все 8 веток? Я не умею расшифровывать / читать / интерпретировать байтовый код, но, возможно, вы ... :) Я уже видел https://github.com/jacoco/jacoco/issues/82, но ни он, ни проблема, на которую он ссылается, очень не помогают (кроме того, что это связано с блоками, сгенерированными компилятором)

Хм, как только я закончил писать это, у меня возникла мысль о том, какие случаи, возможно, не будут проверены тем, что я упомянул выше ... Я отправлю ответ, если я все понял. Я уверен, что этот вопрос и его ответ кому-то поможет в любом случае.

РЕДАКТИРОВАТЬ: Нет, я не нашел. Выбрасывание исключений RuntimeExceptions (не обрабатываемых блоком catch) больше не касалось веток


person Gus    schedule 27.06.2013    source источник
comment
Не могли бы вы выложить файл класса, пожалуйста?   -  person Antimony    schedule 28.06.2013
comment
Нет, я не могу опубликовать код своего клиента.   -  person Gus    schedule 28.06.2013
comment
Лучшее покрытие, которое мне удалось достичь с помощью Eclemma (Эмма в Eclipse), - это 3 из 8 пропущенных ветвей, но Cobertura в Jenkins тогда все еще показывает только 4/8. Будем надеяться, что вскоре эти инструменты покрытия будут правильно обрабатывать try-with-resources.   -  person Torsten Römer    schedule 29.07.2014
comment
Обратите внимание, что многие конструкции, которые JaCoCo не может полностью охватить, такие как эти, предназначены для того, чтобы помочь вам уменьшить количество возможных путей в коде (и, следовательно, ошибок). Стремление к 100% охвату этих вопросов часто невозможно, также это не сильно повлияет на качество вашего тестирования (но требует больших усилий).   -  person Thirler    schedule 05.03.2015
comment
Мой подход заключался в том, чтобы просто переписать мой код, чтобы не использовать предложение try-with-resources. На самом деле это не добавляло особой ценности, учитывая, что это был всего лишь синтаксический сахар и вызывал головную боль при тестировании.   -  person Patrick Michaelsen    schedule 17.12.2019


Ответы (6)


Что ж, я не могу сказать вам, в чем точная проблема с Jacoco, но я могу показать вам, как компилируется Try With Resources. По сути, существует множество сгенерированных компилятором переключателей для обработки исключений, возникающих в различных точках.

Если мы возьмем следующий код и скомпилируем его

public static void main(String[] args){
    String a = "before";

    try (CharArrayWriter br = new CharArrayWriter()) {
        br.writeTo(null);
    } catch (IOException e){
        System.out.println(e.getMessage());
    }

    String a2 = "after";
}

А потом разбираем, получаем

.method static public main : ([Ljava/lang/String;)V
    .limit stack 2
    .limit locals 7
    .catch java/lang/Throwable from L26 to L30 using L33
    .catch java/lang/Throwable from L13 to L18 using L51
    .catch [0] from L13 to L18 using L59
    .catch java/lang/Throwable from L69 to L73 using L76
    .catch [0] from L51 to L61 using L59
    .catch java/io/IOException from L3 to L94 using L97
    ldc 'before'
    astore_1
L3:
    new java/io/CharArrayWriter
    dup
    invokespecial java/io/CharArrayWriter <init> ()V
    astore_2
    aconst_null
    astore_3
L13:
    aload_2
    aconst_null
    invokevirtual java/io/CharArrayWriter writeTo (Ljava/io/Writer;)V
L18:
    aload_2
    ifnull L94
    aload_3
    ifnull L44
L26:
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L30:
    goto L94
L33:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String Object java/io/CharArrayWriter Object java/lang/Throwable
    stack Object java/lang/Throwable
.end stack
    astore 4
    aload_3
    aload 4
    invokevirtual java/lang/Throwable addSuppressed (Ljava/lang/Throwable;)V
    goto L94
L44:
.stack same
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
    goto L94
L51:
.stack same_locals_1_stack_item
    stack Object java/lang/Throwable
.end stack
    astore 4
    aload 4
    astore_3
    aload 4
    athrow
L59:
.stack same_locals_1_stack_item
    stack Object java/lang/Throwable
.end stack
    astore 5
L61:
    aload_2
    ifnull L91
    aload_3
    ifnull L87
L69:
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L73:
    goto L91
L76:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String Object java/io/CharArrayWriter Object java/lang/Throwable Top Object java/lang/Throwable
    stack Object java/lang/Throwable
.end stack
    astore 6
    aload_3
    aload 6
    invokevirtual java/lang/Throwable addSuppressed (Ljava/lang/Throwable;)V
    goto L91
L87:
.stack same
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L91:
.stack same
    aload 5
    athrow
L94:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String
    stack 
.end stack
    goto L108
L97:
.stack same_locals_1_stack_item
    stack Object java/io/IOException
.end stack
    astore_2
    getstatic java/lang/System out Ljava/io/PrintStream;
    aload_2
    invokevirtual java/io/IOException getMessage ()Ljava/lang/String;
    invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
L108:
.stack same
    ldc 'after'
    astore_2
    return
.end method

Для тех, кто не владеет байт-кодом, это примерно эквивалентно следующему псевдо-Java. Мне пришлось использовать gotos, потому что байт-код на самом деле не соответствует потоку управления Java.

Как видите, существует множество случаев для обработки различных возможностей подавленных исключений. Рассматривать все эти случаи неразумно. Фактически, ветвь goto L59 в блоке первой попытки недоступна, поскольку первый перехватчик Throwable перехватит все исключения.

try{
    CharArrayWriter br = new CharArrayWriter();
    Throwable x = null;

    try{
        br.writeTo(null);
    } catch (Throwable t) {goto L51;}
    catch (Throwable t) {goto L59;}

    if (br != null) {
        if (x != null) {
            try{
                br.close();
            } catch (Throwable t) {
                x.addSuppressed(t);
            }
        } else {br.close();}
    }
    break;

    try{
        L51:
        x = t;
        throw t;

        L59:
        Throwable t2 = t;
    } catch (Throwable t) {goto L59;}

    if (br != null) {
        if (x != null) {
            try{
                br.close();
            } catch (Throwable t){
                x.addSuppressed(t);
            }
        } else {br.close();}
    }
    throw t2;
} catch (IOException e) {
    System.out.println(e)
}
person Antimony    schedule 28.06.2013
comment
Да, мне было интересно, действительно ли часть сгенерированного кода недоступна, спасибо. Конечно, было бы неплохо, если бы Oracle улучшила это, или инструменты покрытия учли бы это. - person Gus; 28.06.2013
comment
Отличное объяснение, очень интересно! Теперь я могу перестать гадать, что я упустил. Спасибо! - person Torsten Römer; 29.07.2014
comment
Здесь нет необходимости смотреть на байт-код (хотя это интересное упражнение). JLS определяет, чему эквивалент try-with-resources с точки зрения источника Java: 14.20.3.1. Базовая попытка с ресурсами, которая упрощает просмотр ветвей. - person Joshua Taylor; 20.03.2017
comment
@JoshuaTaylor JLS определяет только семантическую эквивалентность. Вам все еще нужно изучить байт-код, чтобы узнать, использует ли компилятор эту стратегию буквально. Кроме того, вы должны добавить информацию о том, что в настоящее время (обязательно для Java 7) блоки finally копируются для обычного и исключительного случая, что делает тесты избыточными при буквальном использовании указанного шаблона. Как обсуждалось в попробуйте с ресурсами ввести недоступный байт-код, это javac специфическая проблема, например Компилятор Eclipse не создает недостижимый байт-код. - person Holger; 04.03.2019

введите описание изображения здесь

Я могу охватить все 8 веток, поэтому мой ответ - ДА. Посмотрите на следующий код, это всего лишь быстрая попытка, но она работает (или посмотрите мой github: https://github.com/bachoreczm/basicjava и пакет trywithresources, там вы можете узнать, как работает try-with-resources, см. класс ExplanationOfTryWithResources):

import java.io.ByteArrayInputStream;
import java.io.IOException;

import org.junit.Test;

public class TestAutoClosable {

  private boolean isIsNull = false;
  private boolean logicThrowsEx = false;
  private boolean closeThrowsEx = false;
  private boolean getIsThrowsEx = false;

  private void autoClose() throws Throwable {
    try (AutoCloseable is = getIs()) {
        doSomething();
    } catch (Throwable t) {
        System.err.println(t);
    }
  }

  @Test
  public void test() throws Throwable {
    try {
      getIsThrowsEx = true;
      autoClose();
    } catch (Throwable ex) {
      getIsThrowsEx = false;
    }
  }

  @Test
  public void everythingOk() throws Throwable {
    autoClose();
  }

  @Test
  public void logicThrowsException() {
    try {
      logicThrowsEx = true;
      everythingOk();
    } catch (Throwable ex) {
      logicThrowsEx = false;
    }
  }

  @Test
  public void isIsNull() throws Throwable {
    isIsNull = true;
    everythingOk();
    isIsNull = false;
  }

  @Test
  public void closeThrow() {
    try {
      closeThrowsEx = true;
      logicThrowsEx = true;
      everythingOk();
      closeThrowsEx = false;
    } catch (Throwable ex) {
    }
  }

  @Test
  public void test2() throws Throwable {
    try {
      isIsNull = true;
      logicThrowsEx = true;
      everythingOk();
    } catch (Throwable ex) {
      isIsNull = false;
      logicThrowsEx = false;
    }
  }

  private void doSomething() throws IOException {
    if (logicThrowsEx) {
      throw new IOException();
    }
  }

  private AutoCloseable getIs() throws IOException {
    if (getIsThrowsEx) {
      throw new IOException();
    }
    if (closeThrowsEx) {
      return new ByteArrayInputStream("".getBytes()) {

        @Override
        public void close() throws IOException {
          throw new IOException();
        }
      };
    }
    if (!isIsNull) {
      return new ByteArrayInputStream("".getBytes());
    }
    return null;
  }
}
person Community    schedule 01.03.2016
comment
ваш метод autoClose не имеет блока catch. Это не тот случай (и обычно не измеряется охват самого тестового класса?) Кроме того, снимок экрана с выводом jacoco, показывающий, что он покрыт, был бы хорош, если вы хотите заявить об успехе. - person Gus; 01.03.2016
comment
Я приложил снимок экрана, и да, посмотрите охват тестового класса (в строке try-with-resources-end вы увидите 8/8). - person ; 01.03.2016
comment
Я также прикрепил ссылку, где вы найдете точное описание, как работает try-with-resources. - person ; 01.03.2016
comment
Я думаю, что блокировка захвата не имеет отношения к вопросу о покрытии. - person ; 01.03.2016
comment
Так почему бы не добавить ловушку исключения и не устранить все сомнения? - person Gus; 02.03.2016
comment
Я добавил его (чтобы устранить все сомнения, но я не понимаю связи между этой темой и существованием блока catch). - person ; 02.03.2016
comment
Потому что, поскольку в других ответах обсуждается проблема (во время исходного сообщения), связана с тем, как был сгенерирован байт-код. Не сразу очевидно, что байтовый код будет одинаковым с блоком catch и без него. С тех пор многое изменилось, поэтому следующий важный вопрос - какую версию Java и какую версию Jacoco вы использовали? Возможно, байт-код в Java 8 просто более разумен, или может быть, Якоко устал отвечать на эти вопросы и решил перечислить недостижимый байт-код как покрытый ... - person Gus; 02.03.2016
comment
Из вашего ответа и ответа, предоставленного sodamnmad vs Jeff Bennet, также кажется, что может быть разница между байтовым кодом, сгенерированным, когда несколько операторов находятся в try / cach / finally, и одним вызовом метода внутри try / catch / finally. - person Gus; 02.03.2016
comment
Я использовал java 7 и EclEmma 2.3.3, но если есть различия в байт-кодах, я не думаю, что в любом байт-коде оператора try-with-resources есть мертвые коды (что мы не можем покрыть тестами (если у нас есть проверяемый код)). - person ; 02.03.2016
comment
Матиас, я действительно хотел, чтобы вы были правы, но я не могу повторить ваши результаты. Как предполагает Гас, возможно, дело в версии. У меня все еще пропущены 2 из 8 веток. Я использую jacoco-maven-plugin: 0.7.7.201606060606 с JDK 1.8.0_73 и использую JMockit 1.25 и JUnit 4.12. Я верю, что это работает для вас (но не для меня), возможно, потому, что я переработал ваш тестовый код в стиле JMockit или из-за JDK 8 (я) против JDK 7 (вы). - person Jeff Bennett; 06.07.2016

Без вопросов, но я хотел провести больше исследований. tl; dr = Похоже, вы можете достичь 100% покрытия для try-finally, но не для try-with-resource.

Понятно, что существует разница между попыткой «наконец-то старой школы» и «попыткой с ресурсами» Java7. Вот два эквивалентных примера, показывающих одно и то же с использованием альтернативных подходов.

Пример старой школы (попытка окончательно):

final Statement stmt = conn.createStatement();
try {
    foo();
    if (stmt != null) {
        stmt.execute("SELECT 1");
    }
} finally {
    if (stmt != null)
        stmt.close();
}

Пример Java7 (подход с попыткой использования ресурсов):

try (final Statement stmt = conn.createStatement()) {
    foo();
    if (stmt != null) {
        stmt.execute("SELECT 1");
    }
}

Analysis: old-school example:
Using Jacoco 0.7.4.201502262128 and JDK 1.8.0_45, I was able to get 100% line, instruction and branch coverage on the Old School example using the following 4 tests:

  • Базовый путь смазки (оператор не равен нулю, и execute () выполняется нормально)
  • execute () выдает исключение
  • foo () выдает исключение И оператор возвращается как null
  • инструкция возвращена как null
Jacoco indicates 2 branches inside the 'try' (on the null check) and 4 inside the finally (on the null check). All are covered fully.

Анализ: пример java-7:
Если те же самые 4 теста выполняются против примера стиля Java7, jacoco указывает, что охватываются 6/8 ветвей (в самой попытке) и 2/2 при нулевой проверке в рамках попытки. Я попробовал несколько дополнительных тестов для увеличения охвата, но не могу найти лучшего, чем 6/8. Как указывали другие, декомпилированный код (который я также просмотрел) для примера java-7 предполагает, что компилятор java генерирует недоступные сегменты для try-with-resource. Жакоко сообщает (точно), что такие сегменты существуют.

Обновление: используя стиль кодирования Java7, вы можете получить 100% покрытие IF с помощью Java7 JRE (см. ответ Matyas ниже). Однако, используя стиль кодирования Java7 с Java8 JRE, я считаю, что вы попадете в охватываемые ветки 6/8. Тот же код, просто другая JRE. Похоже, что байт-код создается по-разному между двумя JRE, причем Java8 создает недостижимые пути.

person Jeff Bennett    schedule 31.08.2015
comment
Байт-код, создаваемый двумя блоками кода, полностью различается - try-with-resources имеет 3 области обработки исключений, одна из которых начинается перед conn.createStatement(), одна вокруг тела, а другая - сразу после вызова if(stmt != null){ stmt.close(); }. Кроме того, есть вызов Throwable.addSuppressed() и if для защиты от подавления того же исключения. - person earcam; 22.10.2017

Четыре года, но все же ...

  1. Счастливый путь с ненулевым AutoCloseable
  2. Счастливый путь с нулем AutoCloseable
  3. Бросает на запись
  4. Бросает близко
  5. Кидает на запись и закрывает
  6. Выбрасывает спецификацию ресурса (часть with, например, вызов конструктора)
  7. Выбрасывает блок try, но AutoCloseable имеет значение null

Выше перечислены все 7 состояний - причина 8 ветвей связана с повторяющимся состоянием.

Доступны все ветки, try-with-resources - довольно простой сахар компилятора (по крайней мере, по сравнению с switch-on-string) - если они не могут быть достигнуты, то это по определению ошибка компилятора.

Фактически требуется только 6 модульных тестов (в приведенном ниже примере кода throwsOnClose равно @Ingored, а покрытие ветки составляет 8/8.

Также обратите внимание, что Throwable .addSuppressed (Throwable) не может подавить себя, поэтому сгенерированный байт-код содержит дополнительную защиту (IF_ACMPEQ - ссылочное равенство) для предотвращения этого). К счастью, эта ветвь покрывается случаями throw-on-write, throw-on-close и throw-on-write-and-close, поскольку слоты переменных байт-кода повторно используются внешними 2 из 3 областей обработчика исключений.

Это не проблема с Jacoco - на самом деле пример кода в связанном проблема № 82 неверна, так как нет повторяющихся нулевых проверок и нет вложенного блока catch, окружающего закрытие.

Тест JUnit демонстрирует 8 из 8 покрытых веток

import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;

import org.junit.Ignore;
import org.junit.Test;

public class FullBranchCoverageOnTryWithResourcesTest {

    private static class DummyOutputStream extends OutputStream {

        private final IOException thrownOnWrite;
        private final IOException thrownOnClose;


        public DummyOutputStream(IOException thrownOnWrite, IOException thrownOnClose)
        {
            this.thrownOnWrite = thrownOnWrite;
            this.thrownOnClose = thrownOnClose;
        }


        @Override
        public void write(int b) throws IOException
        {
            if(thrownOnWrite != null) {
                throw thrownOnWrite;
            }
        }


        @Override
        public void close() throws IOException
        {
            if(thrownOnClose != null) {
                throw thrownOnClose;
            }
        }
    }

    private static class Subject {

        private OutputStream closeable;
        private IOException exception;


        public Subject(OutputStream closeable)
        {
            this.closeable = closeable;
        }


        public Subject(IOException exception)
        {
            this.exception = exception;
        }


        public void scrutinize(String text)
        {
            try(OutputStream closeable = create()) {
                process(closeable);
            } catch(IOException e) {
                throw new UncheckedIOException(e);
            }
        }


        protected void process(OutputStream closeable) throws IOException
        {
            if(closeable != null) {
                closeable.write(1);
            }
        }


        protected OutputStream create() throws IOException
        {
            if(exception != null) {
                throw exception;
            }
            return closeable;
        }
    }

    private final IOException onWrite = new IOException("Two writes don't make a left");
    private final IOException onClose = new IOException("Sorry Dave, we're open 24/7");


    /**
     * Covers one branch
     */
    @Test
    public void happyPath()
    {
        Subject subject = new Subject(new DummyOutputStream(null, null));

        subject.scrutinize("text");
    }


    /**
     * Covers one branch
     */
    @Test
    public void happyPathWithNullCloseable()
    {
        Subject subject = new Subject((OutputStream) null);

        subject.scrutinize("text");
    }


    /**
     * Covers one branch
     */
    @Test
    public void throwsOnCreateResource()
    {
        IOException chuck = new IOException("oom?");
        Subject subject = new Subject(chuck);
        try {
            subject.scrutinize("text");
            fail();
        } catch(UncheckedIOException e) {
            assertThat(e.getCause(), is(sameInstance(chuck)));
        }
    }


    /**
     * Covers three branches
     */
    @Test
    public void throwsOnWrite()
    {
        Subject subject = new Subject(new DummyOutputStream(onWrite, null));
        try {
            subject.scrutinize("text");
            fail();
        } catch(UncheckedIOException e) {
            assertThat(e.getCause(), is(sameInstance(onWrite)));
        }
    }


    /**
     * Covers one branch - Not needed for coverage if you have the other tests
     */
    @Ignore
    @Test
    public void throwsOnClose()
    {
        Subject subject = new Subject(new DummyOutputStream(null, onClose));
        try {
            subject.scrutinize("text");
            fail();
        } catch(UncheckedIOException e) {
            assertThat(e.getCause(), is(sameInstance(onClose)));
        }
    }


    /**
     * Covers two branches
     */
    @SuppressWarnings("unchecked")
    @Test
    public void throwsOnWriteAndClose()
    {
        Subject subject = new Subject(new DummyOutputStream(onWrite, onClose));
        try {
            subject.scrutinize("text");
            fail();
        } catch(UncheckedIOException e) {
            assertThat(e.getCause(), is(sameInstance(onWrite)));
            assertThat(e.getCause().getSuppressed(), is(arrayContaining(sameInstance(onClose))));
        }
    }


    /**
     * Covers three branches
     */
    @Test
    public void throwsInTryBlockButCloseableIsNull() throws Exception
    {
        IOException chucked = new IOException("ta-da");
        Subject subject = new Subject((OutputStream) null) {
            @Override
            protected void process(OutputStream closeable) throws IOException
            {
                throw chucked;
            }
        };

        try {
            subject.scrutinize("text");
            fail();
        } catch(UncheckedIOException e) {
            assertThat(e.getCause(), is(sameInstance(chucked)));
        }

    }
}

Покрытие Eclipse

Предостережение

Хотя нет в образце кода OP, есть один случай, который не может быть протестирован AFAIK.

Если вы передаете ссылку на ресурс в качестве аргумента, то в Java 7/8 у вас должна быть локальная переменная, которую нужно присвоить:

    void someMethod(AutoCloseable arg)
    {
        try(AutoCloseable pfft = arg) {
            //...
        }
    }

В этом случае сгенерированный код по-прежнему будет охранять ссылку на ресурс. Синтаксический сахар обновлен в Java. 9, где локальная переменная больше не требуется: try(arg){ /*...*/ }

Дополнение - Предложите использовать библиотеку, чтобы полностью избежать ветвлений

По общему признанию, некоторые из этих ветвей можно списать как нереалистичные - то есть там, где блок try использует AutoCloseable без нулевой проверки или где ссылка на ресурс (with) не может быть нулевой.

Часто ваше приложение не заботится о том, где произошел сбой - открыть файл, записать в него или закрыть его - степень детализации сбоя не имеет значения (если приложение специально не связано с файлами, например файловый браузер или текстовый процессор).

Кроме того, в коде OP, чтобы проверить нулевой закрываемый путь - вам нужно будет реорганизовать блок try в защищенный метод, подкласс и предоставить реализацию NOOP - все это просто охватит ветки, которые никогда не будут приняты в дикой природе .

Я написал крошечную библиотеку Java 8 io.earcam.unexceptionalMaven Central), который работает с большинством проверенных шаблонов исключений.

Имеет отношение к этому вопросу: он предоставляет кучу однострочников с нулевым переходом для AutoCloseables, конвертирующих отмеченные исключения в непроверенные.

Пример: Free Port Finder

int port = Closing.closeAfterApplying(ServerSocket::new, 0, ServerSocket::getLocalPort);
person earcam    schedule 22.10.2017
comment
Проблема в том, что вы смотрели на код, сгенерированный Eclipse, чтобы устранить проблемы, вызванные кодом, сгенерированным javac. Было бы немного грубо сказать «если они не могут быть достигнуты, то это по определению ошибка компилятора», поскольку спецификация нигде не гарантирует, что байт-код свободен от недостижимого кода. В нормальных условиях вы бы ничего не заметили. И это не единственное место, где javac генерирует недоступный код, например Я видел устаревшие access$… методы в дикой природе. К счастью, в JDK 11 обе проблемы исчезли. См. Также JDK-8194978. - person Holger; 01.10.2019

Jacoco недавно исправил эту проблему, выпуск 0.8.0 (2018/01/02).

"Во время создания отчетов различные артефакты, сгенерированные компилятором, отфильтровываются, что в противном случае требует ненужных, а иногда и невозможных уловок, чтобы не было частичного или пропущенного покрытия:

  • Часть байт-кода для операторов try-with-resources (GitHub # 500) ".

http://www.jacoco.org/jacoco/trunk/doc/changes.html

person John Bedalov    schedule 19.04.2018

у меня была аналогичная проблема с чем-то вроде этого:

try {
...
} finally {
 if (a && b) {
  ...
 }
}

он жаловался, что 2 из 8 филиалов не были покрыты. в итоге сделал это:

try {
...
} finally {
 ab(a,b);
}

void ab(a, b) {
 if (a && b) {
...
 }
}

никаких других изменений, и теперь я достиг 100% ....

person mdeanda    schedule 29.08.2014
comment
Интересно, хотя это было давно. Возможно, все изменилось, какие инструменты и какие версии вы используете? - person Gus; 30.08.2014
comment
Это не try-with-resources, как указано в вопросе, а скорее try-finally, содержащий условное выражение. - person vallismortis; 01.04.2016