I am trying to generate a code coverage report using Jacoco with JUnit 5 and Spring Boot. And I"m trying to use the DynamicTest feature of JUnit 5. It runs successfully but the Dynamic Tests are not covered in the test coverage report generated by jacoco. Is JUnit 5 Dynamic Test not covered by Jacoco at the moment?
Here is the code:
#RunWith(JUnitPlatform.class)
#SelectPackages("com.troll.jpt.abc.model")
#SelectClasses({Status.class})
public class DynamicModelTester {
private Status status;
#BeforeEach
public void setUp() {
status = new Status();
}
#TestFactory
public Stream<DynamicTest> checkDynamicTestsFromStream() {
List<String> input = Arrays.asList("abc");
List<String> output = Arrays.asList("abc");
status.setCode(input.get(0));
return input.stream().map(str -> DynamicTest.dynamicTest("status test", () -> {
assertEquals(output.get(0), status.getCode());
}));
}
}
The jacoco plugin I'm using is as :
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.2-SNAPSHOT</version>
<executions>
<execution>
<id>prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
<execution>
<id>post-unit-test</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
<configuration>
<!-- Sets the path to the file which contains the execution data. -->
<dataFile>target/jacoco.exec</dataFile>
<!-- Sets the output directory for the code coverage report. -->
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
It runs successfully but the Dynamic Tests are not covered in the test coverage report generated by jacoco.
jacoco-maven-plugin:report doesn't display coverage in tests, it displays coverage of a target of a test - in your case target seems to be class Status, whose definition is completely absent. For future I highly recommend to read https://stackoverflow.com/help/mcve and follow its recommendations, especially part about "Complete":
Make sure all information necessary to reproduce the problem is included
Generic answer on questions like "does JaCoCo support JUnit 5 Dynamic Test?" is: JaCoCo records fact that code was executed independently from the way how it was executed - via JUnit 4 or JUnit 5, or even manually or whatever the other way of execution.
And generic answer on questions like "why some code is not marked as covered?" is: make sure that code is actually executed, in case of automatic tests this means - make sure that these tests are actually executed.
But let's give a try with JUnit 5 Dynamic Test. In absence of src/main/java/Status.java I'll assume that it is something like
public class Status {
private String code;
public void setCode(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
No idea why you need systemPropertyVariables, snapshot version of jacoco-maven-plugin, multiple executions of report and redundant specification of dataFile with its default value, so will also assume that complete pom.xml after slight cleanup looks like
<?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>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-runner</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.1</version>
<configuration>
<outputDirectory>target/jacoco-ut</outputDirectory>
</configuration>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
After placing
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import static org.junit.Assert.assertEquals;
#RunWith(JUnitPlatform.class)
#SelectPackages("com.troll.jpt.abc.model")
#SelectClasses({Status.class})
public class DynamicModelTester {
private Status status;
#BeforeEach
public void setUp() {
status = new Status();
}
#TestFactory
public Stream<DynamicTest> dynamicTestsFromStream() {
List<String> input = Arrays.asList("abc");
List<String> output = Arrays.asList("abc");
status.setCode(input.get(0));
return input.stream().map(str -> DynamicTest.dynamicTest("status test", () -> {
assertEquals(output.get(0), status.getCode());
}));
}
}
into src/test/java/DynamicModelTester.java and execution of mvn clean verify:
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) # example ---
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) # example ---
[INFO] Building jar: /private/tmp/jacoco/target/example-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.8.1:report (default) # example ---
[INFO] Skipping JaCoCo execution due to missing execution data file.
Last line is quite suspicious as well as absence of number of tests executed by maven-surefire-plugin together with absence of target/surefire-reports/.
Let's rename DynamicModelTester into DynamicModelTest and execute mvn clean verify again:
[INFO] --- maven-surefire-plugin:2.21.0:test (default-test) # example ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO] Running DynamicModelTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.023 s - in DynamicModelTest
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) # example ---
[INFO] Building jar: /private/tmp/jacoco/target/example-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.8.1:report (default) # example ---
[INFO] Loading execution data file /private/tmp/jacoco/target/jacoco.exec
[INFO] Analyzed bundle 'example' with 1 classes
Compared to previous attempt test was executed this time. And coverage report target/jacoco-ut/default/Status.html looks like:
I believe that the reason of the fact that test is not executed lies in a default value of includes of maven-surefire-plugin :
A list of elements specifying the tests (by pattern) that
should be included in testing. When not specified and when the test
parameter is not specified, the default includes will be
<includes>
<include>**/Test*.java</include>
<include>**/*Test.java</include>
<include>**/*Tests.java</include>
<include>**/*TestCase.java</include>
</includes>
DynamicModelTester.java doesn't match any of these patters, while DynamicModelTest.java matches second, but I'll leave verification of this as a little exercise.
Related
I setup simple Spring Boot project to serve basic HTML pages. I wrote a kind of integration tests with testNG. Now, I want to run my tests with maven does not see my tests.
STR:
Run mvn spring-boot:run
Run mvn test
Output:
[INFO] Scanning for projects...
[INFO]
[INFO] ---------------------------< vznd:selenium >----------------------------
[INFO] Building selenium 1.0
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) # selenium ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 0 resource
[INFO] Copying 27 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) # selenium ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) # selenium ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory C:\Users\vladyslav.kovalenko\OneDrive - FORM.com\Documents\den\selenium-webdriver-tests\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) # selenium ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:3.0.0-M6:test (default-test) # selenium ---
[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 3.963 s
[INFO] Finished at: 2022-05-09T13:03:51+01:00
[INFO] ------------------------------------------------------------------------
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.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>vznd</groupId>
<artifactId>selenium</artifactId>
<version>1.0</version>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven-surefire-plugin.version>3.0.0-M6</maven-surefire-plugin.version>
<testng.version>7.5</testng.version>
<selenium-java.version>3.141.59</selenium-java.version>
<commons-io.version>2.11.0</commons-io.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium-java.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<systemPropertyVariables>
<webdriver.chrome.driver>/tmp/chromedriver/chromedriver</webdriver.chrome.driver>
</systemPropertyVariables>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
</plugin>
</plugins>
</build>
</project>
testng.xml:
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd" >
<suite name="SeleniumApp Tests" parallel="false">
<test name="Selenium Test">
<classes>
<class name="vznd.selenium.AlertsControllerTest"/>
<class name="vznd.selenium.GettingBrowserInformationTest"/>
<class name="vznd.selenium.FramesTest"/>
<class name="vznd.selenium.BrowserNavigationTest"/>
<class name="vznd.selenium.KeyboardActionsTest"/>
<class name="vznd.selenium.WindowsTest"/>
</classes>
</test>
</suite>
Test example:
package vznd.selenium;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class FramesTest extends BaseTest {
#BeforeMethod
public void openPageWithIframes() {
driver.get(HTMLPath.FRAMES);
}
#Test
public void switchToFrameUsingWebElement() {
WebElement greenFrame = driver.findElement(By.id("first-iframe"));
driver.switchTo().frame(greenFrame);
WebElement table = driver.findElement(By.cssSelector("table[id='green-table']"));
Assert.assertNotNull(table, "The table WebElement object was null!");
}
}
Spring Boot Application:
package vznd.selenium;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SeleniumApp {
public static void main(String[] args) {
SpringApplication.run(SeleniumApp.class, args);
}
}
Controller example:
package vznd.selenium.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
#Controller
public class FramesController {
#GetMapping("/iframes")
public String iframes() {
return "iframes";
}
}
I tried to google about similar problems, but most answers is about naming test classes with *Test suffix and that is not my case.
The tests are executed in IDEA by right-click on the project -> Run -> All tests.
Please, advice me how I can get tests executed with maven.
If you see the logs surefire is not detecting TestNG.
Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider
If you remove SpringBoot will be: Using auto detected provider org.apache.maven.surefire.testng.TestNGProvider
Try adding TestNG dependency to surefire plugin:
<project>
...
<dependencies>
...
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-testng</artifactId>
<version>3.0.0-M5</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<systemPropertyVariables>
<webdriver.chrome.driver>/tmp/chromedriver/chromedriver</webdriver.chrome.driver>
</systemPropertyVariables>
<suiteXmlFiles>
<suiteXmlFile>testng.xml</suiteXmlFile>
</suiteXmlFiles>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-testng</artifactId>
<version>3.0.0-M5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
My project contains a series of Groovy integration test scripts for a few APIs. Previously, these tests executed concurrently and took up to 3 hours to execute. I was able to reduce this time to just 5 minutes by using Spock's parallel execution feature.
The new problem is that the Maven Surefire reports no longer give the correct test counts for each test suite. The total test count is correct (more or less), but Surefire is mixing up which tests go in which report.
Here is my pom:
<?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">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.dsg.payments</groupId>
<artifactId>e2e</artifactId>
<version>0.0.1-SNAPSHOT</version>
<description>This will be used to execute normal credit card End2End test cases</description>
<properties>
<java.version>11</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<build-helper-maven-plugin.version>3.1.0</build-helper-maven-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven-surefire-plugin.version>3.0.0-M3</maven-surefire-plugin.version>
<gmavenplus-plugin.version>1.11.0</gmavenplus-plugin.version>
<swagger-annotations-version>1.6.0</swagger-annotations-version>
<groovy.version>3.0.7</groovy.version>
<spock.version>2.0-M4-groovy-3.0</spock.version>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${groovy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>${spock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>${swagger-annotations-version}</version>
</dependency>
<dependency>
<groupId>org.openapitools</groupId>
<artifactId>jackson-databind-nullable</artifactId>
<version>0.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.findbugs</groupId>
<artifactId>jsr305</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>0.15</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}-${project.version}</finalName>
<testSourceDirectory>${project.basedir}/src/test/groovy</testSourceDirectory>
<resources>
<resource>
<directory>src/test/groovy</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/groovy</directory>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<!-- Groovy compiler -->
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>${gmavenplus-plugin.version}</version>
<executions>
<execution>
<goals>
<goal>addTestSources</goal>
<goal>compileTests</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Java compiler -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<!-- Unit Tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<includes>
<include>**/*.java</include>
</includes>
<skipTests>false</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>
Here is an example of a test class
package com.dsg.payments.e2e.creditcard
import com.dsg.payments.paygate.ApiClient
import com.dsg.payments.paygate.api.AuthorizationApi
import com.dsg.payments.paygate.model.PaygateAuthorizationRequest
import com.dsg.payments.paygate.model.PaygateAuthorizationResponse
import com.dsg.payments.paygate.model.PaygateEncryptedCardTenderResult
import org.junit.Test
import org.springframework.http.HttpStatus
import org.springframework.web.client.HttpClientErrorException
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Title
import spock.lang.Unroll
import util.Constants
import util.CreditCardType
import util.EntityFactory
#Title("CreditCard-Authorization")
class Authorization extends Specification {
// Constants
#Shared
private static final applicationName = Constants.APPLICATION_NAME
#Shared
private static final referer = Constants.REFERER
#Shared
private static final basePath = Constants.BASE_PATH
#Shared
private AuthorizationApi authorizationApi
def setupSpec() {
// Setup ApiClient to make requests to stage AN01
ApiClient authApiClient = new ApiClient()
authApiClient.setBasePath(basePath)
authorizationApi = new AuthorizationApi(authApiClient)
}
#Test
#Unroll
def "Auth request with valid #cardType card returns authorized response"(CreditCardType cardType) {
when:
PaygateAuthorizationResponse authorizationResponse = authorizationApi.authorize(applicationName, EntityFactory.getPaygateAuthorizationRequest(cardType), referer)
PaygateEncryptedCardTenderResult tenderResult = authorizationResponse.getTenderResults().getEncryptedCardTenderResults().get(0)
then:
// No exception thrown by authorization api client means successful 201 response
authorizationResponse != null
where:
cardType << [
CreditCardType.VISA_Credit_1,
CreditCardType.VISA_Debit,
CreditCardType.VISA_Commercial_Debit,
CreditCardType.VISA_Commercial_Credit,
CreditCardType.VISA_Purchasing_Credit,
CreditCardType.VISA_3DS_Not_Enrolled,
CreditCardType.DISCOVER,
CreditCardType.AMERICANEXPRESS_1,
CreditCardType.MASTERCARD_Debit,
CreditCardType.MASTERCARD_Credit,
CreditCardType.MASTERCARD_Premium_Credit,
CreditCardType.DINERS,
CreditCardType.JAPANCREDITBUREAU
]
}
#Test
#Unroll
def "Auth request with valid cvv for #cardType returns cvv response M"(CreditCardType cardType) {
when:
PaygateAuthorizationResponse authorizationResponse = authorizationApi.authorize(applicationName, EntityFactory.getPaygateAuthorizationRequest(cardType), referer)
PaygateEncryptedCardTenderResult tenderResult = authorizationResponse.getTenderResults().getEncryptedCardTenderResults().get(0)
then:
// No exception thrown by authorization api client means successful 201 response
authorizationResponse != null
tenderResult.getCvvResponse() == "M" // Raw CVV result code for "match"
where:
cardType << [
CreditCardType.VISA_Credit_2,
CreditCardType.VISA_Debit,
CreditCardType.VISA_Commercial_Debit,
CreditCardType.VISA_Commercial_Credit,
CreditCardType.VISA_Purchasing_Credit,
CreditCardType.VISA_3DS_Not_Enrolled,
CreditCardType.DISCOVER,
CreditCardType.AMERICANEXPRESS_2,
CreditCardType.MASTERCARD_Debit,
CreditCardType.MASTERCARD_Credit,
CreditCardType.MASTERCARD_Premium_Credit,
CreditCardType.DINERS,
CreditCardType.JAPANCREDITBUREAU
]
}
// ...
}
There are 3 more test classes (Capture, Refund, and Cancel) with a similar structure that use the #Unroll annotation to re-run the same test with different credit card numbers.
Interestingly, when using Spock parallel execution, Surefire adds an extra test to the report for each test method - one for each credit card and an additional test for the method itself. This is not a huge problem in itself, but it is less than ideal. The real problem is that the Surefire report mixes the classes together (for example, Capture tests end up in the Refund, Cancel, or Authorization reports). This makes it hard to tell from the report which tests failed.
I'll also include my SpockConfig.groovy, which configures the parallel execution:
runner {
filterStackTrace false
optimizeRunOrder true
parallel {
enabled true
dynamic(4.0)
}
}
It has taken much trial and error to get the pom in a state where the tests are all executing correctly in parallel fashion. This is the only combination of dependencies, plugins, and versions that works so far. The only thing that is wrong are the Surefire reports. Any ideas what I am missing?
First: I notice the surefire version in the POM shown is 3.0.0-M3. The mvnrepository.com maven-surefire-plugin list shows the latest is 3.0.0-M5 and a lot of the changes in the newer versions appear to be report related.
Second: check the Maven output log to see if the maven-surefire-report-plugin is being used. If it's there, ensure it is using the same version of Surefire as was used to run the tests. You can do that by adding this to the POM:
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-report-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
</plugin>
</plugins>
</pluginManagement>
You may not be using Failsafe for integration tests yet, but it's a good idea to keep the Surefire family of plugins using the same version. So when I need to adjust Surefire version, I set them all in the hope of saving someone future troubleshooting pain.
This is my pom file
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.ricardo</groupId>
<artifactId>cdh</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>cdh</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<azure.version>2.2.0</azure.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.ldap</groupId>
<artifactId>spring-ldap-core</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.1.44</version>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-core</artifactId>
<version>1.1.44</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage</artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-secrets</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-eventhubs</artifactId>
<version>2.3.0</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-spring-boot-bom</artifactId>
<version>${azure.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>local</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<activatedProperties>local</activatedProperties>
</properties>
</profile>
<profile>
<id>dev</id>
<properties>
<activatedProperties>dev</activatedProperties>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<activatedProperties>test</activatedProperties>
</properties>
</profile>
</profiles>
</project>
When I run mvn test this is the output and none of the test run. However every test work as expected using the IDE Intellij.
[INFO] Building cdh 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) # cdh ---
[INFO] Deleting /Users/ricardochampa/mypath/target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) # cdh ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO] Copying 4 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) # cdh ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 140 source files to /Users/mypath/cdh-back/target/classes
[INFO] /Users/mypath/cdh-back/src/main/java/com/ricardo/cdh/mappers/OrdersMapper.java: Some input files use unchecked or unsafe operations.
[INFO] /Users/mypath/cdh-back/src/main/java/com/ricardo/cdh/mappers/OrdersMapper.java: Recompile with -Xlint:unchecked for details.
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) # cdh ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/mypath/cdh-back/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) # cdh ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 6 source files to /Users/mypath/cdh-back/target/test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) # cdh ---
[INFO]
[INFO] -------------------------< com.ricardo:cdh >--------------------------
[INFO] Building cdh 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) # cdh ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO] Copying 4 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) # cdh ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) # cdh ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/mypath/cdh-back/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) # cdh ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) # cdh ---
[INFO]
[INFO] -------------------------------------------------------
[INFO] T E S T S
[INFO] -------------------------------------------------------
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.484 s
[INFO] Finished at: 2020-09-30T22:18:05+02:00
[INFO] ------------------------------------------------------------------------
UPDATE
I've just try with the following pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<include>*Test.java</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
And I tried this as well.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<includes>
<include>customWildcardPattern</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
And this...
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
</plugins>
</build>
And this...
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
</plugins>
</build>
None of them works :(
The project structure.
An example of test class: ProductsServiceTest
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class ProductsServiceTest {
#Mock ProductsRepository productsRepository;
#Mock ResourceService resourceService;
#Autowired #InjectMocks ProductsService service;
private ProductsEntity productsEntity = new ProductsEntity();
private final String stubSearchText = "search this text with results";
private final String stubSearchTextNoResults = "search this text without results";
private final String notFoundStubID = "ID NOT EXISTS";
private final String stubID = "id";
private final String stubName = "name";
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
productsEntity.setId(stubID);
productsEntity.setName(stubName);
when(productsRepository.findById(stubID)).thenReturn(Optional.of(productsEntity));
when(productsRepository.findById(notFoundStubID)).thenReturn(Optional.empty());
doNothing().when(resourceService).changeResourceStatus(any(String.class), any(String.class));
List<ProductsEntity> productsEntityList = new ArrayList<>();
productsEntityList.add(productsEntity);
when(productsRepository.searchProducts(stubSearchText)).thenReturn(productsEntityList);
}
#Test
public void whenGetProduct() throws CdhException {
ProductsDto productsDto = service.getProduct(stubID);
assertEquals(productsDto.getId(), stubID);
assertEquals(productsDto.getName(), stubName);
}
#Test
public void whenGetProductNotExist() {
assertThatThrownBy(() -> service.getProduct(notFoundStubID)).isInstanceOf(CdhException.class);
}
#Test
public void whenCreateProduct() {
ProductsDto dto = new ProductsDto();
dto.setId("fake id");
dto.setIdResource("fake id resource");
service.createProduct(dto);
verify(service.productsRepository).save(any(ProductsEntity.class));
}
#Test
public void whenDeleteProduct() throws CdhException {
service.deleteProduct(stubID);
verify(service.productsRepository).save(any(ProductsEntity.class));
}
}
I finally fix it.
My problems was JUnit4, according to docs https://maven.apache.org/surefire/maven-surefire-plugin/examples/junit.html I should add the plugin like this.
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-junit4</artifactId>
<version>3.0.0-M5</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
1. First potential root cause : Test class naming
Make sure that your test class name matchs one of those default Maven Surefire plugin patterns:
Test*.java
*Test.java
*Tests.java
*TestCase.java
If you need to customize it, you need to add :
<project>
[...]
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<configuration>
<includes>
<include>TO_BE_REPLACED_BY_YOUR_CUSTOM_WILDCARD_PATTERN</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
[...]
</project>
###UPDATE####:
2. Second potential root cause that may lead to not executing test is the exclusion part in your spring-boot-starter-test
Replace:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
by:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
maven surefire plugin is missing in your pom.xml.
Be sure to annotate your test methods with #Test:
import org.junit.jupiter.api.Test; // <-- or whatever you are using..
public class SomeTest {
...
#Test // <----
public void testSomething() {
...
}
...
}
The key here is to understand the test engines that Spring uses.
In my case, I coded API tests with #RunWith(SpringRunner.class), which is under JUnit4, which runs with junit-vintage-engine.
But the unit tests are coded with JUnit Jupiter, which is under JUnit5, which runs with junit-jupiter-engine.
The default engine of SpringBoot is the junit-jupiter-engine. So we have to tell to Spring that we also want to use junit-vintage-engine.
We can simply add the dependency in our pom.xml:
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<scope>test</scope>
</dependency>
Or, if we want to use maven-surefire, we can add the dependency inside the plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M7</version>
<dependencies>
<dependency>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
</plugin>
Despite apparently this post showed a solution to using powermock and jacoco, I haven't been able to make it work in a pretty simple project (available on GitHub).
In my case, the test executes correctly but the jacoco.exec file is missing so jacoco doesn't check coverage.
Test class:
#RunWith(PowerMockRunner.class)
#PrepareOnlyThisForTest(Util.class)
#PowerMockIgnore("org.jacoco.agent.rt.*")
public class UtilTest {
#Test
public void testSay() throws Exception {
PowerMockito.mockStatic(Util.class);
Mockito.when(Util.say(Mockito.anyString())).thenReturn("hello:mandy");
Assert.assertEquals("hello:mandy", Util.say("sid"));
}
}
Util.java
public class Util {
private Util() {}
public static String say(String s) {
return "hello:"+s;
}
}
pom.xml
<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>
<groupId>com.codependent.jacocopower</groupId>
<artifactId>jacoco-powermock</artifactId>
<version>0.0.1-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<systemPropertyVariables>
<jacoco-agent.destfile>target/jacoco.exec</jacoco-agent.destfile>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
</execution>
<execution>
<id>default-restore-instrumented-classes</id>
<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>default-check</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<rules>
<rule>
<element>CLASS</element>
<limits>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>0.50</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<classifier>runtime</classifier>
<version>${jacoco.version}</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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<powermock.version>1.5.4</powermock.version>
<jacoco.version>0.7.4.201502262128</jacoco.version>
</properties>
</project>
Maven execution trace, complaining that no jacoco.exec file was found:
>> mvn clean verify
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building jacoco-powermock 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-clean-plugin:2.5:clean (default-clean) # jacoco-powermock ---
[INFO] Deleting C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) # jacoco-powermock ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:compile (default-compile) # jacoco-powermock ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\classes
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:instrument (default-instrument) # jacoco-powermock ---
[INFO]
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) # jacoco-powermock ---
[WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent!
[INFO] Copying 0 resource
[INFO]
[INFO] --- maven-compiler-plugin:2.5.1:testCompile (default-testCompile) # jacoco-powermock ---
[WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent!
[INFO] Compiling 1 source file to C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\test-classes
[INFO]
[INFO] --- maven-surefire-plugin:2.18.1:test (default-test) # jacoco-powermock ---
[INFO] Surefire report directory: C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\surefire-reports
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running com.codependent.jacoco.UtilTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.239 sec - in com.codependent.jacoco.UtilTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:restore-instrumented-classes (default-restore-instrumented-classes) # jacoco-powermock ---
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:report (default-report) # jacoco-powermock ---
[INFO] Skipping JaCoCo execution due to missing execution data file:C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco.exec
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) # jacoco-powermock ---
[INFO] Building jar: C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco-powermock-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- jacoco-maven-plugin:0.7.4.201502262128:check (default-check) # jacoco-powermock ---
[INFO] Skipping JaCoCo execution due to missing execution data file:C:\SoftDesarrollo\6-Workspaces\libertyGecon\jacoco-powermock\target\jacoco.exec
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.987s
[INFO] Finished at: Thu Jan 21 09:13:55 CET 2016
[INFO] Final Memory: 23M/331M
[INFO] ------------------------------------------------------------------------
UPDATE: (here on GitHub)
With the answer provided by Lencalot the jacoco.exec file is generated but the following scenario keeps saying that the ServiceImpl class coverage is 0%:
ServiceImpl
public class ServiceImpl implements Service{
public String operation() {
return Util.say("Hi!");
}
}
ServiceTest
#RunWith(PowerMockRunner.class)
#PrepareForTest({Util.class})
public class ServiceTest {
private Service service = new ServiceImpl();
#Test
public void testOperation() throws Exception {
PowerMockito.mockStatic(Util.class);
Mockito.when(Util.say(Mockito.anyString())).thenReturn("Bye!");
Assert.assertEquals("Bye!", service.operation());
}
}
Trace:
[WARNING] Rule violated for class com.codependent.jacoco.ServiceImpl: lines covered ratio is 0.00, but expected minimum is 0.50
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<configuration>
<append>true</append>
</configuration>
<executions>
<execution>
<id>default-instrument</id>
<goals>
<goal>instrument</goal>
</goals>
<configuration>
<includes>
<include>**/*test*</include>
</includes>
</configuration>
</execution>
<execution>
<id>default-restore-instrumented-classes</id>
<goals>
<goal>restore-instrumented-classes</goal>
</goals>
<configuration>
<includes>
<include>**/*test*</include>
</includes>
</configuration>
</execution>
<execution>
<id>Prepare-Jacoco</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<excludes>
<exclude>**/*test*</exclude>
</excludes>
</configuration>
</execution>
</executions>
</plugin>
you can remove Check for now, we can try to get it working with out that first.
Update your Jacoco dependency to look like this
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.agent</artifactId>
<version>${jacoco.version}</version>
<scope>test</scope>
</dependency>
Make you maven surefire plugin look like this:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>${argLine}</argLine>
</configuration>
</plugin>
in your properties section Add this as, and change your jacoco Version:
<jacoco.version>0.7.5.201505241946</jacoco.version>
<jacoco.outputDir>${project.basedir}/target/jacoco.exec</jacoco.outputDir>
#codependent Heres an example of why you want to mock.
Lets assume your Util.class has a lot more code, a lot of instantiations and maybe it even touches some server side stuff, hell maybe even its a UI. You don't want to instantiate User.class just to test a single method in some other class called UtilUser.class
protected class UtilUser {
protected UtilUser() {}
public Boolean checkSay(String s) {
String expectedString = "hello:" + s;
String actualString = Util.say(s);
if (expectedString.equals(actualString){
return true;
} else {
return false;
}
}
}
To unit test UtilUser, you want to hit all branches. So in your test you would mock the Util.class, and in one unit test force it to return the correct string, and in another test have it return an incorrect string. You don't care about testing User.class, you know it will behave accordingly or you will have another test to take care of that. For unit testing UtilUser, you can expect Util to either return a correct string or an incorrect string, and you want to test both possibilities.
Theres a time and a place for mocks and they are a great tool to know, but DO NOT think they should be used often. Please refer to the following article, it will give you good insight into when you should use mocks: When To Mock
I'm building up a simple project to learn aspectj.
It's from aspect in action 2nd and the idea is very simple ---- the MessageCommunicator will be responsible for delivering the message and it's the main business logic. Meanwhile, Authenticator will be responsible for authentication and will be weaved as declared SecurityAspect.
Though it's very straightforward to see in the log that the aspect is working. Still I want to ensure it works in junit case.
In my project, I'm using maven 3.0.4 with aspectj 1.7.3 and aspect-maven-plugin 1.5.
Now the problem is below warning is there when compile the test case. As the consequence, the aspects in test package doesn't work. However, if you write a Main class in source package and run, the aspect in source package will work.
The warning message while build:
[INFO] --- aspectj-maven-plugin:1.5:test-compile (test-compile_with_aspectj) # aspectj ---
[WARNING] advice defined in org.javen.study.aspectj.c02.aspects.MockAuthenticationAspect has not been applied [Xlint:adviceDidNotMatch]
[WARNING] advice defined in org.javen.study.aspectj.c02.aspects.SecurityAspect has not been applied [Xlint:adviceDidNotMatch]
[WARNING] advice defined in org.javen.study.aspectj.c02.aspects.TrackingAspect has not been applied [Xlint:adviceDidNotMatch]
I will also attach all the related source code below:
MessageCommunicator who is responsible for the main business:
package org.javen.study.aspectj.c02;
public class MessageCommunicator {
public void deliver(String message) {
System.out.println(message);
}
public void deliver(String person, String message) {
System.out.println(person + ", " + message);
}
}
Simple version of authenticator which will do the authentication:
public class Authenticator {
public void authenticate() {
System.out.println("authenticated");
}
}
SecurityAspect which will advice on MessageCommunicator:
package org.javen.study.aspectj.c02.aspects;
import org.javen.study.aspectj.c02.Authenticator;
public aspect SecurityAspect {
private Authenticator authenticator = new Authenticator();
declare warning
: call(void Authenticator.authenticate())
&& !within(SecurityAspect)
: "Authentication should be performed only by SecurityAspect";
pointcut secureAccess() : execution(* org.javen.study.aspectj.c02.MessageCommunicator.deliver(..));
before() : secureAccess() {
System.out.println("Checking and authenticating user");
authenticator.authenticate();
}
}
MockAuthenticationAspect in test package to advice the authenticator to inject some verification logic(no need to look into advice detail, the advice implementation is problematic):
package org.javen.study.aspectj.c02.aspects;
import org.javen.study.aspectj.c02.Authenticator;
public aspect MockAuthenticationAspect {
declare parents: Authenticator implements Callable;
private boolean Callable.isCalled;
public void Callable.call() {
isCalled = true;
}
public boolean Callable.isCalled() {
return isCalled;
}
Object around(Callable accessTracked) : execution(* Authenticator.authenticate(..))
&& !execution(* Callable.*(..))
&& this(accessTracked) {
accessTracked.call();
return null;
}
private static interface Callable {
}
}
The pom of whole project:
<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>
<groupId>org.javen.study</groupId>
<artifactId>aspectj</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<aspectj.version>1.7.3</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.5</version>
<configuration>
<complianceLevel>1.6</complianceLevel>
<includes>
<include>**/*.java</include>
<include>**/*.aj</include>
</includes>
</configuration>
<executions>
<execution>
<id>compile_with_aspectj</id>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile_with_aspectj</id>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
When build with command "mvn clean install", the warning texts will be printed and the test package advice will not work. However, if check with AJDT in eclipse, all the pointcut and advice is working.
Could someone help me? Thanks a lot.
The problem is solved by added below configuration in test-compile execution.
<execution>
<id>test-compile_with_aspectj</id>
<goals>
<goal>test-compile</goal>
</goals>
<configuration>
<weaveDirectories>
<weaveDirectory>target/classes</weaveDirectory>
</weaveDirectories>
</configuration>
</execution>
Don't know it's a good pratice or not, but at least now it works.