Skip to content
01es edited this page Jun 19, 2024 · 4 revisions

Maven

This documents outlines guidelines for using Maven.

Dependencies

Test scope is not transitive!

TLDR: If module X contains test classes that depend on another module Y and you want to use module X in tests of module Z, create a separate module X-test.

Consider the following example:

  • Module A contains test classes that use dependency D.
  • Module B contains tests that use test classes of A.

For B to work, D must also be on its classpath at runtime.

If you want to limit B's access to test classes of A so that only tests of B can access it, you use:

  • test scope to ensure that the dependency is only available for the test compilation and execution phases
  • test-jar type to indicate that you only need the "tests" part of the dependency
<!-- B -->
<dependency>
  <groupId>example</groupId>
  <artifactId>A</artifactId>
  <scope>test</scope>
  <type>test-jar</type>
</dependency>

A problem will arise if A also uses test scope for D, which will make it unavailable for B at runtime.

There are a few ways to make D accessible to B at runtime:

  1. Declare dependency on D directly in B's POM.
  2. Broaden the scope of D in A's POM.

Approach 2 has the benefit of being simpler to maintain: B doesn't have to know about A's dependencies. With this approach, A's POM contains the following:

<!-- A -->
<dependency>
  <groupId>example</groupId>
  <artifactId>D</artifactId>
</dependency>

The default scope is compile, which is transitive. This effectively establishes a relationship between B and D to be B -> A (test) -> D (compile), which is equivalent to B -> D (test). Refer to the dependency scope matrix for details.

Had A declared test scope for D, it would not be visible to B, and ClassNotFoundException would occur at runtime.

The only disadvantage of this approach is the reduced encapsulation: D becomes accessible to main sources of A.

In general, it is better to extract reusable test classes into a separate module. In the example above, we would extract reusable test classes of A into main sources of a new module A-test and declare a dependency on D with scope compile. Then, B would depend on A-test with scope test.

<!-- A-test -->
<dependency>
  <groupId>example</groupId>
  <artifactId>D</artifactId>
</dependency>

<!-- B -->
<dependency>
  <groupId>example</groupId>
  <artifactId>A-test</artifactId>
  <scope>test</scope>
</dependency>

References:

Clone this wiki locally