Issue
I have a complex Maven build, and I would like to have some automated check that the JAR files and POM files produced by the build actually contain what I expect them to contain.
For instance, I would like to check for the presence of an Automatic-Module-Name
entry in the manifest file. It's a multi-release JAR, so I would like to check that the proper class files exist inside of META-INF/versions
. I'm going to publish to Maven Central, so I'd also like to check that the produced pom file contains the dependencies the project needs, but that the produced pom file for the fat jar that I also publish, doesn't contain these dependencies.
Basically, I'd like to unit test my build :).
Unfortunately, it's hard to google for this, because of the words I would use to describe this ("test", "verify") already have very specific different meanings in Maven.
Is there a nice way to do this? I would prefer a Maven plugin, since I'm obviously already using that, but I'm open to other things too.
Solution
I ended up, as @khmarbaise suggested in the comments, creating a new Maven submodule. In its pom I used the copy-rename-maven-plugin
to copy over the files I actually want to check, like this:
<plugin>
<groupId>com.coderplus.maven.plugins</groupId>
<artifactId>copy-rename-maven-plugin</artifactId>
<version>${version.copy-rename-maven-plugin}</version>
<executions>
<execution>
<id>copy-artifacts</id>
<phase>compile</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<fileSets>
<fileSet>
<sourceFile>${project.basedir}../core/target/.flattened-pom.xml</sourceFile>
<destinationFile>${project.basedir}/src/test/resources/flattened.pom</destinationFile>
</fileSet>
<fileSet>
<sourceFile>${project.basedir}../core/target/myArtifactId-${project.version}.jar</sourceFile>
<destinationFile>${project.basedir}/src/test/resources/myArtifactId.jar</destinationFile>
</fileSet>
<!-- more fileSets here -->
</fileSets>
</configuration>
</execution>
</executions>
</plugin>
Then I was able to read the pom file and do assertions on it. I ended up using Java's built-in XPath API, but you can use whatever.
I was also able to read the JAR file by turning it into a NIO FileSystem:
var filename = "myArtifactId.jar"; // or "flattened.pom"
var file = getClass().getClassLoader().getResource(filename);
var uri = URI.create("jar:" + file.toURI().toString());
FileSystem fs = FileSystems.newFileSystem(uri, Map.of());
You can get a list of files:
var path = fs.getPath("/");
Set<String> filenames = StreamSupport
.stream(walk.spliterator(), false)
.map(Path::toString)
.collect(Collectors.toSet());
Or read the content of a file:
var path = fs.getPath("/META-INF/MANIFEST.MF");
var out = new ByteArrayOutputStream();
Files.copy(path, out);
String content = out.toString();
assertTrue(content.contains("Multi-Release: true"));
var path = fs.getPath("/com/example/MyClass.class");
var out = new ByteArrayOutputStream();
Files.copy(path, out);
byte[] content = out.toByteArray();
var actualVersion = content[7]; // the major version of the class file is at this location
assertEquals(52, actualVersion); // 52 = Java 8
(Note that for this answer, I didn't bother to handle exception or close resources; you'll have to do that yourself.)
Answered By - jqno
Answer Checked By - Clifford M. (JavaFixing Volunteer)