2017-02-22 36 views
0

У меня есть довольно многомодульная сборка проектов Maven. Я начинаю со всех наших тестов с помощью PowerMock и используя Jacoco 0.7.8 и автономную аппаратуру.Изменено JaCoCo от офлайн до онлайн, но до сих пор не видно покрытия для классов в других модулях

Несмотря на то, что они называются «модульными испытаниями», тесты для классов в одном модуле требуют значительного кода в других модулях. В результате, когда мы рассматриваем сгенерированные отчеты, мы видим покрытие для классов в том же модуле, что и тест, но мы не видим покрытия для классов в других модулях, которые были выполнены во время теста.

Мое ПРЕДПОЛОЖЕНИЕ заключается в том, что если я смогу изменить это для использования онлайн-инструментов, в итоговый отчет о покрытии будут включены классы из других модулей за пределами текущего модуля.

Итак, я приступил к фиксации одной детали в наших тестах (класс под тестом), который требовал использования PowerMock вместо Mockito (эта часть была довольно простой). Я исправил это по одному в каждом тесте и соответствовал CUT, но пока я сделал эти изменения только в одном модуле в более крупной сборке. Я проверил, что тесты работают, и что я могу видеть интерактивное покрытие с помощью EclEmma (еще одно ограничение PowerMock).

Плагин jacoco-maven сконфигурирован в родительском помпе, который используется всеми дочерними модулями, чтобы в настоящее время использовать автономную аппаратуру. В конце концов, я собираюсь изменить конфигурацию в этом родительском pom, чтобы использовать онлайн-инструменты, но я решил оставить это там до сих пор и переопределить конфигурацию в каждом модуле, в котором я сделал преобразование, так что этот модуль использует онлайн-инструменты. Я считаю, что я сделал это «правильно», хотя мне пришлось использовать hack, чтобы переопределить список выполнения в дочернем модуле (помните, что я собираюсь удалить их, как только все они будут преобразованы).

При запуске сборки («clean install„), я вижу это сделать“prepare-agent», напечатав в результате „argLine“ значения, то в дальнейшем безошибочном выполняется, то „report“ цель выполняет, печать пути к jacoco.exec файл, и я вижу, что он говорит «Analyzed bundle 'my-module-name' with 1 classes». Это кажется показателем проблемы.

Поскольку сборка была завершена, я открыл файл «target/site/jacoco/index.html» в своем браузере, и я обнаружил, что он включает только покрытие для настоящего модуля и только один класс.

Было также любопытно, что я поверхностно осмотрел сгенерированный файл «jacoco.exec». Я заметил, что это определенно больше, чем я привык видеть из других модулей, которые все еще используют автономную аппаратуру. У меня нет возможности интерпретировать формат файла, но я попробовал просто «cat» в файле, чтобы посмотреть, что я вижу. Я видел строки, которые представляли имена классов в других модулях, которые выполняются моими тестами.

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

Возможно ли получить это покрытие?

Ниже приводится отрывок из «эффективного» п выхода:

<plugin> 
    <groupId>org.jacoco</groupId> 
    <artifactId>jacoco-maven-plugin</artifactId> 
    <version>0.7.8</version> 
    <executions> 
     <execution> 
     <id>default-instrument</id> 
     <phase>none</phase> 
     <goals> 
      <goal>instrument</goal> 
     </goals> 
     </execution> 
     <execution> 
     <id>default-restore-instrumented-classes</id> 
     <phase>none</phase> 
     <goals> 
      <goal>restore-instrumented-classes</goal> 
     </goals> 
     </execution> 
     <execution> 
     <id>default-report</id> 
     <phase>prepare-package</phase> 
     <goals> 
      <goal>report</goal> 
     </goals> 
     </execution> 
     <execution> 
     <id>prepare-agent</id> 
     <goals> 
      <goal>prepare-agent</goal> 
     </goals> 
     </execution> 
    </executions> 
    </plugin> 
    <plugin> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.19.1</version> 
    <executions> 
     <execution> 
     <id>default-test</id> 
     <phase>test</phase> 
     <goals> 
      <goal>test</goal> 
     </goals> 
     <configuration> 
      <argLine>@{argLine} -Xmx1024m</argLine> 
      <includes> 
      <include>**/*Test.java</include> 
      </includes> 
      <systemPropertyVariables> 
      <running-unit-test>true</running-unit-test> 
      <jacoco-agent.destfile>...\target/jacoco.exec</jacoco-agent.destfile> 
      </systemPropertyVariables> 
     </configuration> 
     </execution> 
    </executions> 
    <configuration> 
     <argLine>@{argLine} -Xmx1024m</argLine> 
     <includes> 
     <include>**/*Test.java</include> 
     </includes> 
     <systemPropertyVariables> 
     <running-unit-test>true</running-unit-test> 
     <jacoco-agent.destfile>...\target/jacoco.exec</jacoco-agent.destfile> 
     </systemPropertyVariables> 
    </configuration> 
    </plugin> 

Обратите внимание, что расстрелы, используемые для офлайновых приборов имеют фазы «none», поэтому они не используются, и я также устанавливая свойство «jacoco-agent.destfile», которое используется только для автономной аппаратуры.

ответ

0

Goal report выполняет анализ классов только в текущем модуле и создает отчет, содержащий только их, даже если данные были записаны для других классов. И goal report-aggregate

отчет покрытие из нескольких проектов в реакторе

(см ниже, например)

Кроме того, возвращаясь к PowerMockito - как было сказано в https://stackoverflow.com/a/42305077/244993 это добавляет дополнительную сложность, однако:

a/src/main/java/example/A.java:

package example; 

class A { 
    // to be tested in module "a" 
    void a() { 
    System.out.println("A_a" + fun()); 
    } 

    // to be tested in module "b" 
    void b() { 
    System.out.println("A_b" + fun()); 
    } 

    // to be mocked by PowerMockito 
    static String fun() { 
    return ""; 
    } 
} 

a/src/test/java/example/ATest.java:

package example; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Mockito; 
import org.powermock.api.mockito.PowerMockito; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

@PrepareForTest({A.class}) 
@RunWith(PowerMockRunner.class) 
public class ATest { 
    @Test 
    public void test() { 
    PowerMockito.mockStatic(A.class); 
    Mockito.when(A.fun()).thenReturn("fun"); 

    new A().a(); 
    } 
} 

b/src/main/java/example/B.java:

package example; 

class B { 
    // to be tested in module "b" 
    void b() { 
    System.out.println("B_b" + fun()); 
    } 

    // to be mocked by PowerMockito 
    static String fun() { 
    return ""; 
    } 
} 

b/src/test/java/example/BTest.java:

package example; 

import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.Mockito; 
import org.powermock.api.mockito.PowerMockito; 
import org.powermock.core.classloader.annotations.PrepareForTest; 
import org.powermock.modules.junit4.PowerMockRunner; 

@PrepareForTest({A.class, B.class}) 
@RunWith(PowerMockRunner.class) 
public class BTest { 
    @Test 
    public void test() { 
    PowerMockito.mockStatic(A.class); 
    Mockito.when(A.fun()).thenReturn("fun"); 

    PowerMockito.mockStatic(B.class); 
    Mockito.when(B.fun()).thenReturn("fun"); 

    new A().b(); 
    new B().b(); 
    } 
} 

a/pom.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <parent> 
    <groupId>org.example</groupId> 
    <artifactId>example</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    </parent> 

    <artifactId>a</artifactId> 
</project> 

b/pom.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <parent> 
    <groupId>org.example</groupId> 
    <artifactId>example</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    </parent> 

    <dependencies> 
    <dependency> 
     <groupId>org.example</groupId> 
     <artifactId>a</artifactId> 
     <version>${project.version}</version> 
    </dependency> 
    </dependencies> 

    <artifactId>b</artifactId> 
</project> 

report/pom.xml для агрегирования покрытия через модули:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <parent> 
    <groupId>org.example</groupId> 
    <artifactId>example</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    </parent> 

    <artifactId>report</artifactId> 
    <packaging>pom</packaging> 

    <dependencies> 
    <dependency> 
     <groupId>org.example</groupId> 
     <artifactId>a</artifactId> 
     <version>${project.version}</version> 
    </dependency> 
    <dependency> 
     <groupId>org.example</groupId> 
     <artifactId>b</artifactId> 
     <version>${project.version}</version> 
    </dependency> 
    </dependencies> 

    <build> 
    <plugins> 
     <plugin> 
     <groupId>org.jacoco</groupId> 
     <artifactId>jacoco-maven-plugin</artifactId> 
     <executions> 
      <execution> 
      <id>report</id> 
      <phase>prepare-package</phase> 
      <goals> 
       <goal>report-aggregate</goal> 
      </goals> 
      </execution> 
     </executions> 
     </plugin> 
    </plugins> 
    </build> 
</project> 

и, наконец, самая важная часть - pom.xml, где происходит все волшебство:

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 
    <modelVersion>4.0.0</modelVersion> 

    <groupId>org.example</groupId> 
    <artifactId>example</artifactId> 
    <version>1.0-SNAPSHOT</version> 
    <packaging>pom</packaging> 

    <properties> 
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 
    <maven.compiler.source>1.8</maven.compiler.source> 
    <maven.compiler.target>1.8</maven.compiler.target> 

    <jacoco.version>0.7.9</jacoco.version> 
    <powermock.version>1.6.6</powermock.version> 
    </properties> 

    <modules> 
    <module>a</module> 
    <module>b</module> 
    </modules> 

    <dependencies> 
    <dependency> 
     <groupId>junit</groupId> 
     <artifactId>junit</artifactId> 
     <version>4.8.2</version> 
     <scope>test</scope> 
    </dependency> 

    <dependency> 
     <groupId>org.powermock</groupId> 
     <artifactId>powermock-module-junit4</artifactId> 
     <version>${powermock.version}</version> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.powermock</groupId> 
     <artifactId>powermock-api-mockito</artifactId> 
     <version>${powermock.version}</version> 
     <scope>test</scope> 
    </dependency> 
    </dependencies> 

    <build> 
    <plugins> 
     <plugin> 
     <groupId>org.apache.maven.plugins</groupId> 
     <artifactId>maven-jar-plugin</artifactId> 
     <version>3.0.2</version> 
     </plugin> 
    </plugins> 
    </build> 

    <profiles> 
    <profile> 
     <id>coverage</id> 
     <modules> 
     <module>report</module> 
     </modules> 
     <dependencies> 
     <dependency> 
      <groupId>org.jacoco</groupId> 
      <artifactId>org.jacoco.agent</artifactId> 
      <classifier>runtime</classifier> 
      <version>${jacoco.version}</version> 
      <scope>test</scope> 
     </dependency> 
     </dependencies> 
     <build> 
     <plugins> 
      <plugin> 
      <groupId>org.jacoco</groupId> 
      <artifactId>jacoco-maven-plugin</artifactId> 
      <version>${jacoco.version}</version> 
      <executions> 
       <execution> 
       <id>instrument</id> 
       <phase>process-classes</phase> 
       <goals> 
        <goal>instrument</goal> 
       </goals> 
       </execution> 
       <execution> 
       <id>report</id> 
       <!-- 
       restore original classes before generation of report, 
       but after packaging of JAR: 
       --> 
       <phase>post-integration-test</phase> 
       <goals> 
        <goal>restore-instrumented-classes</goal> 
        <goal>report</goal> 
       </goals> 
       </execution> 
      </executions> 
      </plugin> 
      <plugin> 
      <groupId>org.apache.maven.plugins</groupId> 
      <artifactId>maven-surefire-plugin</artifactId> 
      <version>2.12.2</version> 
      <configuration> 
       <systemPropertyVariables> 
       <jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile> 
       </systemPropertyVariables> 
      </configuration> 
      </plugin> 
     </plugins> 
     </build> 
    </profile> 
    </profiles> 
</project> 

в такой установке

mvn clean verify -Pcoverage 

сгенерирует

  • a/target/site/jacoco/index.html содержащего покрытие для классов в модуле a испытаний в модуле a
  • b/target/site/jacoco/index.html, содержащее покрытие для классов в модуле b испытаний в модуле b
  • report/target/site/jacoco-aggregate/index.html, содержащее покрытие для классов модули a и b испытаниями в модулях a и b

но полученные файлы JAR содержат инструментальные классы, однако

mvn package -DskiptTests -Dmaven.jar.forceCreation 

будет производить не-инструментальную JARs.

Эта сложность не нужна в случае, если вы избавитесь от PowerMockito.В этом случае coverage профиль в pom.xml может быть упрощена:

<profile> 
    <id>coverage</id> 
    <modules> 
    <module>report</module> 
    </modules> 
    <build> 
    <plugins> 
     <plugin> 
     <groupId>org.jacoco</groupId> 
     <artifactId>jacoco-maven-plugin</artifactId> 
     <version>${jacoco.version}</version> 
     <executions> 
      <execution> 
      <goals> 
       <goal>prepare-agent</goal> 
       <goal>report</goal> 
      </goals> 
      </execution> 
     </executions> 
     </plugin> 
    </plugins> 
    </build> 
</profile> 

и mvn clean verify -Pcoverage будет генерировать те же отчеты и баночками, которые не требуют восстановления.

+0

Что мне действительно нужно понять, если все тесты для модуля являются mockito и с использованием онлайн-инструментов jacoco, как тесты для модуля показывают покрытие для классов вне модуля? Я предполагаю, что, поскольку я использую цель «слияния» для создания объединенного файла jacoco.exec, мой вывод sonarqube будет иметь весь охват, но я хотел бы видеть это покрытие в сгенерированном отчете jacoco. Есть ли способ получить это? –

+0

@ DavidM.Karr На данный момент ни одного слова, ни о «SonarQube», ни о «объединении» в описании вашего вопроса нет. Как уже было сказано [несколько] (http://stackoverflow.com/questions/42302786/jacoco-only-shows-coverage-for-classes-in-the-same-module/42305077#42305077) [раз] (https: //groups.google.com/d/msg/jacoco/bFJlLT0sROk/9riq_jz9BgAJ) - сделайте попытку предоставить [полный пример] (http://stackoverflow.com/help/mcve), чтобы получить ответ, который точно соответствует вашему делу. Кстати, вы пытались выполнить «report-aggregate» в объединенном файле? – Godin

+0

Я не упоминал sonarqube, потому что у меня нет никаких проблем с этим. Насколько я могу судить, он берет данные из объединенного файла exec, который имеет данные для классов за пределами вызывающего модуля в каждом случае. И да, я также использую агрегат report-aggregate, о котором я не упоминал в этом сообщении, потому что я сосредоточился на сборке каждого модуля, а не на агрегации, но я также отмечаю, что в отчете-агрегате также отсутствует покрытие для классы, которые достигаются извне модуля. В каждом случае покрытие является «мелким». –