Gradle test fixtures plugin and core module dependencies
Asked Answered
D

1

16

I have a project built with Gradle version 6.4 and JDK 8. I'm trying to use the Gradle plugin for Test Fixtures (java-test-fixtures) but I have some issues with the dependencies.

According to the Gradle page linked above, the project should be structured like this:

core-module
-- src
   -- main
      -- java
   -- test
      -- java
   -- testFixtures
      -- java

While the build.gradle.kts file has the following dependencies section:

dependencies {
    api("com.my.external.project:1.0")
    // ... more API dependencies

    testFixturesCompileOnly(project(":core-module"))
    testFixturesApi("junit:junit:4.12")
    // ... more test dependencies
}

Now, in IntelliJ (the IDE I'm using) classes in the testFixtures/java source folder see the classes in the main/java source folder. So I can add new Java classes under testFixtures/java that have dependencies on those under main. However, I won't be able to import the dependencies from the external library com.my.external.project:1.0. The problem is confirmed when I try to run the Gradle task compileTestFixturesJava.

I can duplicate the entry in the dependencies section; e.g. I can add:

testFixturesImplementationOnly("com.my.external.project:1.0")

But that is not really what I expect to do; especially when I have dozens of dependencies.

I could also define the dependencies in an array and run a for-each over them. Still, this is not the cleanest solution.

Is there a clean solution that will allow the testFixtures module to use the dependencies declared in the main module?

Dominic answered 30/9, 2020 at 7:41 Comment(18)
Any reason you're not just using a multiproject build?Parse
This is how it is structured in my company and I can't change it.Dominic
@Dominic Correct me if my understand is wrong -- is the error here that the Gradle task compileTestFixturesJava fails without a duplicated entry because it (for whatever reason) is not drawing from the already-specified dependencies declared as api/implementation under the dependencies tag (available to main), causing unresolved classes? Also, would you be able to provide a minimalist reproduction repository?Wachter
I have the same problem, if you know how to solve it, please tell me howPrimateship
@Wachter the answer to your first question is yes (if I get you right). For the second, I may not find the time. Let's see.Dominic
I created a simple project here github.com/mricciuti/so-64133013 but could not reproduce your issue: in this example, testFixture classes can see main dependencies (here :commons-lang3) : if you analyse dependencies you will see that testFixturesCompileClasspath inherits from main dependencies . maybe you could compare this simple project to yours and check what's different?Chlorothiazide
@Chlorothiazide class github.com/mricciuti/so-64133013/blob/master/core-module/src/… in test fixtures is not using JUnit, for instance, nor any code from the main moduleDominic
@Dominic 1) "classes in test fixtures is not using JUnit" :indeed, but they are not supposed to! (and if you really need this, just add junit dependency in testFixturesImplementation configuration. 2) "nor any code from the main module" ==> you're wrong: Simpsons class in test Fixture references Person class from main sourcesSet..Chlorothiazide
@Dominic Are you satisfied with the solution already proposed in the comments? Am I correct in thinking it wouldn't be worth anybody else expending any more effort beyond that? To be honest, I'd be surprised if you received any better answers — if that's what you're holding out for.Redmond
@Dominic If you are able to provide a minimal reproduction, I can take a detailed look into this. I do not think I would be able to accurately guess your current build configuration.Wachter
@Chlorothiazide why do you say that they are not supposed to use dependencies from the test module? The Gradle page doesn't even says you have to add a dependency from the main module, as you did. For me there is no point in having a testFixtures folder under the another module if the plugin doesn't explicitly inherit from the main module. The fact I also have to import dependencies for testing explicitly for the fixtures folder is stupid, since most likely I will write utilities using JUnit, AssertJ and other testing tools.Dominic
@Redmond no, I'm ok. Disappointed on how poor is the documentation for that plugin and that it is not so flexible as it could.Dominic
@Chlorothiazide thanks for the POC and comment. You could turn it into an answer, so tha we can shape that fr future devs that will jump in here.Dominic
@Dominic you're right, it makes sense to have test framework dependencies in testFixtures scope (e.g. if you want to setup some shared mock instances using mockito), personally I use testFixtures only for setting up domain entity instances for test purpose, but your use case is perfectly valid. in this case, just declare your needed dependencies in testFixturesImplementation or testFixturesApi scope. regarding dependency from testFixture to main sourceSet it's automatically set up by the plugin so you don't need to declare it : I did it just because I copied your initial exampleChlorothiazide
I'll provide soon an answer with an enriched version of my previous POC, to show a valid example based on your last comment. cheersChlorothiazide
…how poor is the documentation for that plugin…“ — I agree. When I've been stumped by Gradle issues, somebody either in their forums or on their Slack channel have rescued me. — „…it is not so flexible as it could…“ — I think that plugin's goal is to be X-Unit agnostic; to facilitate fixtures meant to be (re)used in different projects. One project might use TestNG. Another might use Spock, etc. If JUnit 5's baked into the testFixture then it probably wouldn't be as portable across different projects.Redmond
Have you tried this: sourceSets { testFixtures { compileClasspath += sourceSets.main.output runtimeClasspath += sourceSets.main.output } } (sorry for the bad formatting)Postdoctoral
…Have you tried this: sourceSets { … }“ – @Postdoctoral — Isn't that already being configured implicitly by JavaPlugin.configureSourceSets(JavaPluginConvention, BuildOutputCleanupRegistry) and JavaTestFixturesPlugin.createImplicitTestFixturesDependencies(Project, JavaPluginConvention)?Redmond
C
24

Most important concept in the Gradle java-test-fixtures plugin is stated in their documentation:

[this plugin] will automatically create a testFixtures source set, in which you can write your test fixtures. Test fixtures are configured so that:

  • they can see the main source set classes
  • test sources can see the test fixtures classes

This plugin will indeed create the following dependencies: main <-- testFixtures , and testFixtures <-- test

In your case, testFixtures module should automatically depend on main sources, and also on main dependencies declared in api scope ( com.my.extenal.project:1.0)

See a similar example in a valid sample project here https://github.com/mricciuti/so-64133013 :

  • Simpsons class has access to Person class from main module
  • TestHelpers class has access to main dependencies declared in api configuration

Note that testFixtures will not inherit dependencies from the test module: if you need to use such libraries in this module (eg. JUnit, Mockito, ...) you will need to declare explicit dependency , using testFixturesImplementation or testFixturesApi configuration.

See example in core-module

plugins {
    id ("java-library")
    id ("java-test-fixtures")
}
dependencies {
    // Main dependencies
    //   will be available in "testFixture" as well thanks to "api" configuration
    api("org.apache.commons:commons-lang3:3.9")
    api(project(":utils-module"))

    // Testfixture dependencies
        // ==> mockito lib will be available in testFixture module and also in consumer modules (e.g test)
    testFixturesApi("org.mockito:mockito-core:3.5.13")

    // Test dependencies
       // dependencies specific to the "test" module; not visible from "testFixtures"
    testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
    testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.3.1")
}
Chlorothiazide answered 11/10, 2020 at 8:58 Comment(1)
It was testFixturesApi(project(":utils-module")) that saved the day for meAlansen

© 2022 - 2024 — McMap. All rights reserved.