4

Unit testing a Maven build

 2 years ago
source link: https://jqno.nl/post/2022/03/30/unit-testing-a-maven-build/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Unit testing a Maven build

March 30, 2022

2 minute read

A few weeks ago, I asked a question on StackOverflow: how can you test a Maven build?

EqualsVerifier a complex Maven build, and I want to have some automated checks that the jar files and pom files produced by the build actually contain what I expect them to contain.

For instance, I want to check for the presence of an Automatic-Module-Name entry in the manifest file. It’s a multi-release jar, so I also want to check that the proper class files exist inside of the jar file’s META-INF/versions directory. Finally, EqualsVerifier is published to Maven Central, so I also want 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.

Unfortunately, it’s hard to google for this, because of the words I would use to describe this (“test”, “verify”) already have specific different meanings in Maven.

I got a nice response from Karl Heinz Marbaise, one of the Maven devs, who suggested I create an additional Maven submodule, use a plugin to copy the relevant artifacts to a directory, and go from there.

So I created the equalsverifier-release-verify submodule in the project and used copy-rename-maven-plugin to copy the files, as follows:

<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>${artifact.src.main}/.flattened-pom.xml</sourceFile>
                        <destinationFile>${artifact.dst}/equalsverifier-main.pom</destinationFile>
                    </fileSet>
                    <fileSet>
                        <sourceFile>${artifact.src.main}/equalsverifier-${project.version}.jar</sourceFile>
                        <destinationFile>${artifact.dst}/equalsverifier-main.jar</destinationFile>
                    </fileSet>
                    <!-- more fileSets here -->
                </fileSets>
            </configuration>
        </execution>
    </executions>
</plugin>

Now the poms and jars are in the src/test/resources folder, I can work with them. For the pom files, I used Java’s built-in XPath API, because it’s simple, and my needs are simple as well. But you can use whatever you want.

For the jar files, I used NIO to access their content as a 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());

Now I can get a list of files that exist in the jar:

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 to check the content of the manifest:

var path = fs.getPath("/META-INF/MANIFEST.MF");
var out = new ByteArrayOutputStream();
Files.copy(path, out);
String content = out.toString();
assertTrue(content.contains("Automatic-Module-Name: nl.jqno.equalsverifier"));

I can even check if files are compiled to the correct Java version:

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

(Here is a description of Java’s class file format, including a list of Java major class file versions.)

Note that for this post, I didn’t bother handling exceptions or closing resources. Filling in those blanks is left as an exercise for you, dear reader 😉.

This was a pretty fun thing to play around with! If you want to see the full code, take a look at the equalsverifier-release-verify submodule on GitHub!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK