Hi All, A question about the JVM Test Suite Plugin...
# dependency-management
i
Hi All, A question about the JVM Test Suite Plugin's handling of dependencies. I can see the
testImplementation
configuration
extendsFrom
the project's
implementation
configuration (and in turn,
api
). However, for any other test suite I add, that's not the case, and the docs show adding an
implementation(project())
dependency instead. I believe I understand why the project dependency isn't added automatically, i.e. to give build authors more flexibility for their test suite dependencies. My question is, why a project dependency instead of extending from the project's
implementation
configuration like the built-in unit test suite does? I'm looking to more thoroughly understand the design difference.
t
I think it's easier and more understandable for build maintainers to add
integrationTestImplementation(project)
. Or do you mean why not do that same thing internally with the
test
source set?
e
I think it makes sense to use
implementation(project())
for integration test suites, as you're consuming the project more like an external user than an internal one (with access to
compileOnly
etc.)
👍 1
1
i
@tony, exactly, the latter. I'm trying to understand the internal difference more, and any pros or cons to one or the other. (Coincidentally, I was asking to deepen my understanding before responding to @Tapchicoma's explanation of the change that lead to our KT-70871 issue.)
👍 1
t
I had a feeling 🙂
😆 1
the answer may not be any more complex than "there is and always has been a million possible ways to do anything in gradle, and this is the way we chose to do it on that day way back when"
I think that adding that
project
dependency would I think have the same effect as what KGP is doing without also breaking DAGP, so I'm a big fan! It would make all the main deps transitive deps in the new source set, rather than direct
although having said that, I still think not adding any deps by default is even better
i
Yeah, I understand KGP is doing it in response to my use of
associateWith()
(https://kotlinlang.org/docs/gradle-configure-project.html#associate-compiler-tasks). I use that so integration tests can see Kotlin
internal
elements from the main source set. But that's done automatically for the unit test suite without mutating
testImplementation
, so why the need to mutate
integrationTestImplementation
? I'm wondering whether adding an
extendsFrom
or using the project dependency could satisfy whatever their need is instead, and not break DAGP.
j
Is there any scenario in which the integration tests (or any test-like sources) of a project are not going to need the project's main sources? Looks weird to me that this is not the default behavior.
e
some integration test could treat the project as a complete black box, and (for example) run it as a separate binary, not using the sources as all
1
t
@Ian Brandt while I still think they're doing it in the wrong way, i wonder if DAGP can detect the call to associateWith? i missed that you were doing that. if I'm following correctly, that means this isn't default behavior for all new source sets?
i
Correct. By default, the Kotlin compiler/KGP does not consider Gradle source sets other than the special-case
test
source set to be part of the same module as the
main
source set, and so they can't access
internal
API: https://kotlinlang.org/docs/visibility-modifiers.html#local-declarations.
associateWith()
is the API KGP offers to make other source sets (or I guess it's compilations) "friends" with the
main
source set (compilation): https://kotlinlang.org/docs/gradle-configure-project.html#associate-compiler-tasks.
I'm going to try to adapt the example @Jendrik Johannes posted to back out all the
implementation
configuration dependencies KGP is adding to `integrationTestImplementation`: https://github.com/autonomousapps/dependency-analysis-gradle-plugin/issues/1239#issuecomment-2363141328. I'm not sure whether that'll break something KGP needs. I'll report back.
👍 1
j
Its mostly about wiring everything through dependency management. Ideally, even the regular test suite would work with a project dependency, though we tried this many times in the past and some insufficiencies of the resolution engine keeps stopping us: https://github.com/gradle/gradle/pull/28453 The advantage of this sort of pattern is that dependency-management is aware of what is on a classpath. For example we have two options: 1. testClasspath = configurations.testRuntimeClasspath.plus(sourceSets.test.output) 2. testClasspath = configuration.testRuntimeClasspath + testImplementation(project) With the second option, dependency management knows your project is on the test classpath. It can do things like detect and resolve capability conflicts. Like, for example, if your production code is a fat jar, we would be able to detect if you declared a dependency on one of the things you're fat jarring. The first option doesn't do this. Furthermore, this improves reporting. Tools will not "see" the project on the first option, but will see the project on the test classpath using the second option. Finally, this clean up wiring. With the first option, you need to mess with the "internals" of the production component. Its source sets, compilation tasks, jars, etc. With the second, you operate on the "public api" of the production code, which is its published variants. Using project dependencies like this makes sure you aren't reaching into the internal wiring of the project but instead rely on what the project chooses to expose (its variants)
thank you 2
i
Thank you, Justin! That was a very helpful explanation. It's great to get deeper insight into Gradle's design.