Maven Plugin Testing - In a Modern way - Part VI

In the previous part of the series - Maven Plugin Testing - In a Modern way - Part V we have seen how to define system properties to run Maven. In this part we will take a deeper look how we can define profile(s) for a Maven call to be used.

Let us take a look at a simple example taken from the previous part.

1@MavenJupiterExtension
2class BaseIT {
3
4  @MavenTest
5  @MavenGoal("verify")
6  void the_first_test_case(MavenExecutionResult result) {
7     ...
8  }
9}

This will run Maven with the goal verify. In real life it makes sometimes sense to run integration tests only by activating a profile like this:

1mvn verify -Prun-its

So the question is now: How can we do that in the Integration Testing Framework? This can be achieved by using the @MavenProfile annotation like this:

 1@MavenJupiterExtension
 2class BaseIT {
 3
 4  @MavenTest
 5  @MavenGoal("verify")
 6  @MavenProfile("run-its")
 7  void the_first_test_case(MavenExecutionResult result) {
 8     ...
 9  }
10}

This would execute our integration with the following command line (except the things we mentioned in previous parts of the series; Just omitted them for brevity.):

1mvn verify -Prun-its

In the following example you can see that the @MavenProfile annotation used only on the method first:

 1@MavenJupiterExtension
 2class BaseIT {
 3
 4  @MavenTest
 5  @MavenGoal("verify")
 6  @MavenProfile("run-its")
 7  void first(MavenExecutionResult result) {
 8     ...
 9  }
10
11  @MavenTest
12  void second(MavenExecutionResult result) {
13     ...
14  }
15
16  @MavenTest
17  void third(MavenExecutionResult result) {
18     ...
19  }
20}

This will execute all above integration test cases except the first without a profile.

The @MavenProfile annotation can be put on different methods for running a different profile within different test cases like this:

 1@MavenJupiterExtension
 2class BaseIT {
 3
 4  @MavenTest
 5  @MavenGoal("verify")
 6  @MavenProfile("run-its")
 7  void first(MavenExecutionResult result) {
 8     ...
 9  }
10
11  @MavenTest
12  void second(MavenExecutionResult result) {
13     ...
14  }
15
16  @MavenTest
17  @MavenProfile("run-e2e")
18  void third(MavenExecutionResult result) {
19     ...
20  }
21}

The above example would execute the test case third with the given profile run-e2e activated on command line and of course the first test case with the given profile run-its.

There are cases where it is needed to run a bunch of integration tests with the same profile which can be achieved by defining the @MavenProfile annotation on the class level like the following:

 1@MavenJupiterExtension
 2@MavenProfile("run-its")
 3class BaseIT {
 4
 5  @MavenTest
 6  @MavenGoal("verify")
 7  void first(MavenExecutionResult result) {
 8     ...
 9  }
10
11  @MavenTest
12  void second(MavenExecutionResult result) {
13     ...
14  }
15
16  @MavenTest
17  void third(MavenExecutionResult result) {
18     ...
19  }
20}

This will result in executing all three test cases using the same profile. Now let us think about another scenario. You have defined a set of integration tests (let us assume 25) and create the appropriate test class based on the pattern of the above. Now you have to add a single test case which should not use the profile which is defined on the class level. It should use a different one.

There are two solutions for this. The first one would be to create a separate integration test class which defines the appropriate profile on the class- or on the method level (it is a matter of taste). The other solution would be to define another test case method with another @MavenProfile annotation on it as the following example shows:

 1@MavenJupiterExtension
 2@MavenProfile("run-its")
 3class BaseIT {
 4
 5  @MavenTest
 6  @MavenGoal("verify")
 7  void first(MavenExecutionResult result) {
 8     ...
 9  }
10
11  @MavenTest
12  @MavenProfile("run-somethingdifferent")
13  void second(MavenExecutionResult result) {
14     ...
15  }
16
17  @MavenTest
18  void third(MavenExecutionResult result) {
19     ...
20  }
21}

The result will be to execute the test cases first and third with the -Prun-its profile and the second test case will be executed with the run-somethingdifferent profile. In other words it means the defined profiles are not additive. Let me show an example of a limitation of this concept. If you would like to have the second test case executed without a profile at all this is not possible. This would lead into the solution having to define a separate class instead.

Apart from that there are cases where it is useful to define several profiles on a Maven build like this:

1mvn -Prun-its,run-e2e

This can also done via using several @MavenProfile annotations on the appropriate test case (or on the class level) like this:

 1@MavenJupiterExtension
 2class BaseIT {
 3
 4  @MavenTest
 5  @MavenGoal("verify")
 6  @MavenProfile("run-its")
 7  @MavenProfile("run-e2e")
 8  void the_first_test_case(MavenExecutionResult result) {
 9     ...
10  }
11}

So based on the previous parts of the series you could conclude that it is possible to define your own meta annotation to make combinations easier.

1@Target({ElementType.METHOD, ElementType.TYPE})
2@Retention(RUNTIME)
3@Inherited
4@MavenProfile("run-its")
5@MavenProfile("run-e2e")
6public @interface ExecuteIntegrationAndEndToEnd {
7}

This is not limited to the @MavenProfile annotation. You can combine that with the annotation like @MavenGoal etc. as mentioned in the previous articles of the series.

So this it is for Part VI. 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 project which shows the previous example can be found on GitHub.