Maven Plugin Testing - In a Modern way - Part II
In the first part of the series - Maven Plugin Testing - In a Modern way - Part I we have seen how to make the basic setup with The Integration Testing Framework and run very basic integration test.
In this second part we will take a deeper look into other aspects of testing Maven plugins in particular how we check the logging output of a Maven build process.
Let us begin with writing more than a single integration test case. You can of course write multiple test cases within a single test class like the following:
1@MavenJupiterExtension
2class SeveralMavenIT {
3
4 @MavenTest
5 void the_first_test_case(MavenExecutionResult result) {
6 ...
7 }
8 @MavenTest
9 void the_second_test_case(MavenExecutionResult result) {
10 ...
11 }
12 @MavenTest
13 void the_third_test_case(MavenExecutionResult result) {
14 ...
15 }
16}
Apart from the test cases them self we need the according projects which are used as test projects which looks like this:
1.
2βββ src/
3 βββ test/
4 βββ resources-its/
5 βββ org/
6 βββ it/
7 βββ SeveralMavenIT/
8 βββ the_first_test_case/
9 β βββ src/
10 β βββ pom.xml
11 βββ the_second_test_case/
12 β βββ src/
13 β βββ pom.xml
14 βββ the_this_test_case/
15 βββ src/
16 βββ pom.xml
So after we have executed the integration tests (mvn verify
) the resulting
directory structure will look like this:
1.
2βββtarget/
3 βββ maven-it/
4 βββ org/
5 βββ it/
6 βββ SeveralMavenIT/
7 βββ the_first_test_case/
8 β βββ .m2/
9 β βββ project/
10 β β βββ src/
11 β β βββ target/
12 β β βββ pom.xml
13 β βββ mvn-stdout.log
14 β βββ mvn-stderr.log
15 β βββ other logs
16 βββ the_second_test_case/
17 β βββ .m2/
18 β βββ project/
19 β β βββ src/
20 β β βββ target/
21 β β βββ pom.xml
22 β βββ mvn-stdout.log
23 β βββ mvn-stderr.log
24 β βββ mvn-arguments.log
25 βββ the_third_test_case/
26 βββ .m2/
27 βββ project/
28 β βββ src/
29 β βββ target/
30 β βββ pom.xml
31 βββ mvn-stdout.log
32 βββ mvn-stderr.log
33 βββ mvn-arguments.log
Based on the resulting directory structure you can see each test case
completely separated from each other. This means also that each test case contains
it's own maven cache (.m2/repository
). You can find also the separated log file outputs and
the separate project
directory which contains the test project after the test run. That
is very helpful for later issue analysis.
So now let us take a deeper look into the test cases:
1 @MavenTest
2 void the_first_test_case(MavenExecutionResult result) {
3
4 }
In each test you have seen a parameter to the test method MavenExecutionResult result
.
The injected parameter gives you access to the result of the test build of project.
This class contains appropriate methods to access the result of the build process, the project cache, the project itself (in other words to the directory) and of course to the different logging output files which have been created during the run of the test.
So the first thing you usually check would be if the built has been successful or not. This depends on the type of integration test you are writing. This can be achieved by using the following:
1assertThat(result).isSuccessful();
This expects the built being successful as you already suspected. You can of course write a test which assumes that your built must fail which can be expressed like this:
1assertThat(result).isFailure();
So the isSuccessful()
means the return code 0
whereas isFailure()
represents
a return code which is not 0
.
You can combine the check for a successful build and no warning output like this:
1assertThat(result)
2 .isSuccessful()
3 .out()
4 .warn().isEmpty();
So .out()
will access the created output file of the build mvn-stdout.log
. The .warn()
will filter out all lines which start with [WARNING]
. The .isEmpty()
is part of AssertJ
framework for assertion against lists which implies that the result is empty.
So now let us check some output which is produced by a usual build. The output emits [INFO]
so the test
can use .out().info().
instead which looks like this:
1@MavenJupiterExtension
2class FirstIT {
3 void base_test (MavenExecutionResult result) {
4 assertThat(result)
5 .isSuccessful()
6 .out()
7 .info()
8 .containsSubsequence(
9 "--- maven-enforcer-plugin:3.0.0-M1:enforce (enforce-maven) @ kata-fraction ---",
10 "--- jacoco-maven-plugin:0.8.5:prepare-agent (default) @ kata-fraction ---",
11 "--- maven-resources-plugin:3.1.0:resources (default-resources) @ kata-fraction ---",
12 "--- maven-compiler-plugin:3.8.1:compile (default-compile) @ kata-fraction ---",
13 "--- maven-resources-plugin:3.1.0:testResources (default-testResources) @ kata-fraction ---",
14 "--- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ kata-fraction ---",
15 "--- maven-surefire-plugin:3.0.0-M4:test (default-test) @ kata-fraction ---",
16 "--- maven-jar-plugin:3.2.0:jar (default-jar) @ kata-fraction ---",
17 "--- maven-site-plugin:3.9.1:attach-descriptor (attach-descriptor) @ kata-fraction ---"
18 );
19 }
20}
The .containsSubsequence(..)
checks that the sequence
is in the correct order with optional supplemental parts in between.
While writing plugins/extensions it sometimes happens that you emit an information on WARN Level to give some hints what needs to be mentioned but will not fail a build.
The maven-jar-plugin for example will emit a warning if no content will be added into the jar. This could be checked like the following:
1assertThat(result)
2 .isSuccessful()
3 .out()
4 .warn()
5 .contains("JAR will be empty - no content was marked for inclusion!");
So this can be combined to create a more comprehensive test case like this:
1@MavenJupiterExtension
2class FailureIT {
3
4 @MavenTest
5 void basic_configuration_checking_logout(MavenExecutionResult result) {
6 assertThat(result)
7 .isSuccessful()
8 .out()
9 .info()
10 .containsSubsequence(
11 "--- maven-enforcer-plugin:3.0.0-M1:enforce (enforce-maven) @ basic_configuration_checking_logout ---",
12 "--- jacoco-maven-plugin:0.8.5:prepare-agent (default) @ basic_configuration_checking_logout ---",
13 "--- maven-resources-plugin:3.1.0:resources (default-resources) @ basic_configuration_checking_logout ---",
14 "--- maven-compiler-plugin:3.8.1:compile (default-compile) @ basic_configuration_checking_logout ---",
15 "--- maven-resources-plugin:3.1.0:testResources (default-testResources) @ basic_configuration_checking_logout ---",
16 "--- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ basic_configuration_checking_logout ---",
17 "--- maven-surefire-plugin:3.0.0-M4:test (default-test) @ basic_configuration_checking_logout ---",
18 "--- maven-jar-plugin:3.2.0:jar (default-jar) @ basic_configuration_checking_logout ---",
19 "--- maven-site-plugin:3.9.1:attach-descriptor (attach-descriptor) @ basic_configuration_checking_logout ---"
20 );
21 assertThat(result)
22 .isSuccessful()
23 .out()
24 .warn()
25 .contains("JAR will be empty - no content was marked for inclusion!");
26
27 }
28}
If you write a plugin which contains a parameter for encoding an output like this (might look familiar to you) should be produced:
1Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!
This can be checked within a test case like this:
1assertThat(result)
2 .out()
3 .warn()
4 .containsExactly("Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent!");
So this it is for Part II. If you like to learn more about the Integration Testing Framework you can consult the users guide. If you like to know the state of the release you can take a look into the release notes.
If you have ideas, suggestions or found bugs please file in an issue on github.
An example can be found on GitHub.