The duplicate finder plugin comes with a comprehensive integration test suite to ensure its behavior.
Integration tests are executed using the Maven Invoker Plugin. The test suite comes with a set of tools that makes writing integration tests simple.
Writing an integration test for the duplicate finder plugin
Integration tests are located in the src/it
folder of the source tree. Any directory that starts with test-
contains an integration test. All integration tests are always executed as part of the plugin build process.
Any integration test directory must contain at least three files:
pom.xml
- The project POM, which must inherit from the base POMinvoker.properties
- The goals to invoke and the expected outcome.verify.groovy
- The result verification script. All integration tests use groovy.
Tests that add code, compile classes etc. may have more files.
The integration test POM
Any integration test must inherit from the base POM which sets up the plugin correctly. That ensures that any global change in the future will be picked up by all unit tests.
The POM for any integration test should look like this:
<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>
<parent>
<groupId>@project.groupId@.@project.artifactId@</groupId>
<artifactId>basepom</artifactId>
<version>1.0.under-test</version>
</parent>
<artifactId>... the artifact id of the test ...</artifactId>
<description>... brief description what the test tries to accomplish ...</description>
... additional POM elements ...
</project>
Any integration test must use the name of its directory as the artifactId. As integration test directory names must start with test-
, any integration test artifactId must be test-....
.
The invoker.properties
file
All integration tests should contain almost identical invoker.properties
files:
invoker.goals=clean verify
invoker.buildResult = success
for a successful plugin execution
invoker.goals=clean verify
invoker.buildResult = failure
for an unsuccessful plugin execution.
The integration test should still write a duplicate plugin result file which then is verified using the verify.groovy
script
The verify.groovy
script
Almost all integration tests will use the result file output after a plugin run to check the outcome. The integration test suite contains a library which makes these checks reasonably simple.
The ITools
library is based on the XmlSlurper library; additional processing can be done with the tools listed there.
The source code for the ITools
library is in the main source tree. Also, the existing integration tests serve as examples on how to write tests and verify plugin results.
Any verify.groovy
script should start with
import static org.basepom.mojo.duplicatefinder.groovy.ITools.*
def result = loadTestXml(basedir)
...
This loads the result file information for the test
classpath scope into the result
variable.
Any test that needs to validate from a different scope or must access elements that are not enclosed by a result
element need to use
def (result, xml) = loadXmlAndResult(basedir, "runtime")
Here the xml
variable will contain the full result file element tree and result
will contain the test result for the runtime
scope.
Checking the general outcome of an integration test:
overallState(conflictState, count, failState, result)
conflictState
can beNO_CONFLICT
,CONFLICT_EQUAL
orCONFLICT_DIFF
(constants are defined in theITools
library)failState
can beFAILED
orNOT_FAILED
(constants are defined in theITools
library)count
is the number of elements in conflict state. Note that multiple conflicts between the same elements (e.g. thefirst-jar
andsecond-jar
) only count as one.result
is the value returned byloadTestXml
orloadXmlAndResult
orloadXml
.
Do a conflict check:
checkConflictResult(conflictName, conflictType, conflictState, excepted, printed, failState, conflictResult)
conflictName
is the class or resource name that is in conflict.conflictType
can beTYPE_CLASS
orTYPE_RESOURCE
(constants are defined in theITools
library)conflictState
can beNO_CONFLICT
,CONFLICT_EQUAL
orCONFLICT_DIFF
(constants are defined in theITools
library)excepted
can beNOT_EXCEPTED
orEXCEPTED
(constants are defined in theITools
library). Anexcepted
conflict is covered by an<exception>
element from the configuration.printed
can beNOT_PRINTED
orPRINTED
(constants are defined in theITools
library). APRINTED
conflict was reported on the command line.failState
can beFAILED
orNOT_FAILED
(constants are defined in theITools
library). AFAILED
conflict also failed the build.
The following tools help build the conflictResult
value:
findConflictResult(result, String ... matches)
findConflictResult(result, count, String ... matches)
-
result
is the value returned byloadTestXml
orloadXmlAndResult
orloadXml
. -
count
is the number of expected result elements that match all of thematches
elements. If omitted, 1 is assumed. -
matches
A list of element names that match the test results. -
For the predefined jars, the Match name (see below) can be used.
-
For a match from the local project, the
projectTargetFolder(basedir)
andprojectTargetTestFolder(basedir)
tools can be used.
Jars for duplication checks
As the duplicate finder plugin deals with a classpath elements, there are a number of pre-built jars that can be used in an integration test:
Maven GAV coordinates | Contents | Match name | Usage |
---|---|---|---|
testjar:first-class-jar:1.0.under-test |
diff.Demo class |
FIRST_CLASS_JAR |
Use with first-diff-jar to find classpath class duplicates with different SHA |
testjar:second-class-jar:1.0.under-test |
demo.Demo class |
SECOND_CLASS_JAR |
Use with second-equal-jar to find classpath class duplicates with same SHA |
testjar:first-diff-jar:1.0.under-test |
diff.Demo class, different SHA from first-class-jar |
FIRST_DIFF_JAR |
|
testjar:second-equal-jar:1.0.under-test |
demo.Demo class, same SHA as second-class-jar |
SECOND_EQUAL_JAR |
|
testjar:first-jar:1.0.under-test |
conflict-same-content and conflict-different-content resources |
FIRST_JAR |
Use with second-jar to find classpath resource duplicates with the same or different SHA values. |
testjar:second-jar:1.0.under-test |
conflict-same-content with the same SHA and conflict-different-content with a different SHA from first-jar |
SECOND_JAR |
Debugging an integration test
As integration tests are intended to highlight an aspect of the plugin functionality, they are extremely useful for debugging / isolating of problems. To debug the plugin while executing an integration test, use the following steps:
- build and install the plugin locally, prepare the integration tests:
mvn -Dinvoker.test='setup*' clean install
. This step is required only once until the next timemvn clean
is executed. - now build and run the integration test to debug:
mvn -Dinvoker.mavenOpts='-Xdebug -Xnoagent -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000' -Dinvoker.test='<testname>' invoker:integration-test
. This will execute the integration test and suspend the VM. A debugger can now attach to port 8000 to debug the plugin execution.
If wildcards are given for the tests to run, a new JDK will be spawned for each integration test and the debugger must be attached multiple times.
If the plugin code has changed, either combine the test
goal with invoker:integration-test
or use the proper integration-test
lifecycle goal.