Help understanding publishing artifact, plain vs. bootable, and testImplementation,

My Goal

I have been trying to understand and get something to work for a few days now and am wondering if anyone can help me accomplish what I want to do with Gradle 7.6.1 in a Java Sprint Boot set of projects.

  • I have a common completely separate Java Spring Boot project that houses common code and configuration.
  • The build.gradle publishes the main project jar to be used and shared which is working fine.
  • I am trying to now publish a new artifact as a jar dependency that just holds the src/test/resources (think flyway SQL migrations & other config) which will be used by other projects as a dependency with testImplementation for integration testing with an H2 DB.

What I have Tried

Common Project
In the common project’s build.gradle I define and publish the artifact as follows (I have tried a lot of different ways to publish a dependency holding test/resources but this seems to be the most straight forward/cleanest):

task createTestResourcesJar(type: Jar) {
    from sourceSets.test.resources
}

artifacts {
    archives createTestResourcesJar
}

publishing {
    publications {
        // main common publish
        mavenJava(MavenPublication) {
            groupId = 'com.foobar'
            artifactId = 'foobar-common'
            from components.java
        }
        // test resources publish
        testResources(MavenPublication) {
            groupId = 'com.foobar'
            artifactId = 'common-test-resources'
            version = "0.0.1-SNAPSHOT"
            from components.java
            artifact createTestResourcesJar
        }
...
  • When the publish is completed both a bootable jar and plain jar are created for test resource; however, only the bootable jar has the test/resources contents, the plain jar does not. The jars I see published are:
    • common-test-resources-0.0.1-SNAPSHOT.jar has test/resources contents
    • common-test-resources-0.0.1-SNAPSHOT-plain.jar has the whole project classes not test/resources
  • I have tried various ways to force the same content in both jars but have only been successful in getting the plain jar to have that content when I force the publish to use classified ‘plain’ without the use of ‘from components.java’ but then bootable jar is not created
  • I have also forced the publish to only create the bootable jar successfully too and not the plain jar using classified ‘boot’
  • Not having both jars created seems like it is a problem later in the other project trying to use the dependency in testImplementation which I explain next

Project that Wants to use the test/resources dependency
In the downstream project’s build.gradle

  • I use testImplementation ‘com.foobar:common-test-resources:0.0.1-SNAPSHOT’
  • The test/resources contents are not found
  • When I print the test classpath I see the -plain.jar only on the classpath which doesn’t have the test/resouces
  • When I had not generated the plain or bootable, and only generated one of them, the jar file seems to be ignored and not placed on the test classpath at all by testImplementation?
  • So it seems you have to have both the bootable and plain published for testImplementation to be able to put anything on the test classpath???
  • With both jars generated I tried using this and other syntax to indicate to use the bootable jar but I just got gradle errors indicating this was not valid (also tried appending :classified:boot and just boot:
  testImplementation 'com.foobar:common-test-resources:0.0.1-SNAPSHOT' {
    classified 'boot'
   }

Can anyone offer any assistance please? I have spent hours on what I think should be something simple to do - very humbling…

Thanks

I’m sorry I didn’t read all the text.
But from a cursory look you might be after the java test fixtures plugin which allows you to easily publish a separate variant with test fixtures like flyway migrations, helper classes, and so on, that downstream projects then can easily request and use.

I spent a chunk of time investigating java test fixtures plugin… I could not get it to do what I wanted and got a lot of errors - the docs are not the best in describing publishing your own fixtures or I am just not that experienced with gradle to understand it + not finding a lot of examples online.

Regardless I finally did figure out an easy solution to my problem in case this helps anyone else. I concluded from different readings that the gradle spring boot plugin is what causes the plain and boot jars to be created. I was trying to force the plain jar to have the same content as the boot jar and failing or trying to pick the boot jar when using testImplementation without any success…

I found archiveClassifier and wondered if that could be the answer and it was. Below is how I got the contents of the jar file to be test/resources and then how I referenced the published dependency in the other project in order to use the shared test/resources…

Common Project build.gradle

task createTestResourcesJar(type: Jar) {
    from sourceSets.test.resources
    archiveClassifier = 'test' // causes -test.jar to be created
}

artifacts {
    archives createTestResourcesJar
}

publishing {
    publications {
        // main common publish
        mavenJava(MavenPublication) {
            groupId = 'com.foobar'
            artifactId = 'foobar-common'
            from components.java
        }
       // test resources publish
       testResources(MavenPublication) {
           groupId = 'com.foobar'
          artifactId = 'common-test-resources'
          version = "0.0.1-SNAPSHOT"
          from components.java
          artifact createTestResourcesJar
       }
...

Downstream Project’s build.gradle

testImplementation 'com.foobar:common-test-resources:0.0.1-SNAPSHOT:test' // references specific -test.jar