Hi, I am using a gradle for building java (xtend) ...
# community-support
h
Hi, I am using a gradle for building java (xtend) test code containing multiple projects. There are base test classes that are cross referenced by other projects. Other projects lists their dependencies on the base class using "testImplementation project("reference to base class project""). Earlier we were using gradle 5.1 and everything seems to be fine and the dependencies are resolved automatically. We had to upgrade to gradle 7.4.2 and now the cross referencing no more works and the build fails unable to cross reference definitions used from base test class. For time being the dependencies are copied to the projects creating multiple copies of same base test classes in individual projects but we were wondering what changed between 5.1 and 7.4.2 which can make this not working. Any pointers here would be helpful. Thanks.
c
I'm not certain what version of gradle it was added, but I think it should be there in 7.x. This sounds like a case for test fixtures. Put your base class in a test fixtures and then depend on that. This of course may depend on what the base class really is... if it doesn't depend on a project itself then just give it its own subproject
h
Hi, Let me be more clear on my issue. I have a project A which has sub-Projects Y and Z. Sub project Y has test base classes defined under src/test/java which are abstract class, with some functions defined. Sub Project Z has test classes which extends the classes defined in sub Project Y and refer to functions defined in abstract class defined in Project Y. The problem with cross referencing doesnt exist when 5.1 gradle is used, but when we upgraded to 7.4.2, it complains that the definitions are not found. I am new to gradle but if i understand correctly test fixtures are for sharing the test data across multiple sub projects. The issue here is the base test classes which are extended in a sub project are not able to identify that they are defined in another sub project. Anything with an example would be a great help to us. Thanks.
c
A test fixture can mean whatever you really want it to mean. Usually I use them for creating test data builders. Arguably they are for any class that you want to be shared amongst multiple test suites. I think a test Base class is fine there.
You could also do it the same way that the test fixture plugin does it if you wanted to give it another name.
h
Hi Caleb, could you please help me with an example on how to define test fixtures in sub projects Y and Z and create a dependency between them. I tried the way suggested in https://blog.cronn.de/en/testing/java/gradle/2023/04/20/gradle-testfixtures.html, however its not clear from it on how to define testFixturesImplementation in the sub project containing the base abstract class.
c
There is a Gradle doc on this but I'll be back in 15-20 minutes and see if I can help you then if you're still not getting it
h
Ok...I would also like to give more information here. All the base classes are defined in sub project Y under <Y>/src/test/java/A/B/C/D folder. All the java files are written in xtend and there is <Y>/src/xtend-gen/A/B/C/D where all the java files are stored. I am confused on how to define the "testFixturesImplementation" in such a case.
c
so this is not an unreasonable example, although you probably don't have access to jvm test suites yet. https://github.com/xenoterracide/spring-app-commons/blob/main/commons-jpa/build.gradle.kts#L36-L39
in project Y you need to add the
test-fixtures
plugin and then create
src/testFixtures/java/A/B...
and move your abstract class to the appropriate package
you will add any dependencies in project Y more or less like I did in line36-39 although yours will have to be written in groovy (I'm guessing) without the version catalog static accessors so..
testFixtureImplementation("org.example:foo")
then in project Z you need to depend on project Y's test fixture
testImplementation(testFixture(project(":Y")))
I think it is
and in project Y you depend on your fixtures as
testImplementation(testFixture(project))
, that all make sense?
h
Ok...I will need to check how to copy the abstract classes under /src/testFixtures/......though..Will try and update here on my findings. Thanks a lot for your suggestions.
c
you can treat
testFixtures
the same as you would
main
and
test
directories, it's just another sourceSet
oh, are you saying you have a sourceSet called xtend-gen?
I don't know if those were available in 7...
h
sourceSets { main { java.srcDirs = ['src/main/java', 'src/main/xtext-gen', 'src/main/src-gen', 'src/main/openapi-gen'] resources.srcDirs = ['src/main/resources', 'src/main/xtext-gen'] xtendOutputDir = 'src/main/xtend-gen' } test { java.srcDirs = ['src/test/java', 'src/test/xtext-gen'] resources.srcDirs = ['src/test/resources', 'src/test/xtext-gen'] xtendOutputDir = 'src/test/xtend-gen' } }
this is how sourceSets are defined in my project
c
yeah... I have to say I have no idea how to deal with that...
testFixtures is just another srcDir essentially
but I suspect you're going beyond my knowledge.
@Vampire
v
Actually your
srcDirs
declarations look very fishy anyway. It looks like you have some tasks that generate code and there configure the paths to their outputs and then adding explicit
dependsOn
for some of the tasks needing sources. This is not really a good idea. Instead you should make sure the generation tasks do properly declare their inputs and outputs (as always) and then configure those tasks directly as
srcDir
which makes their outputs considered sources and automatically adds an implicit task dependency to all tasks that need source files, including compilation tasks, javadoc tasks, source jar tasks, static code analyzers, ..... Practically any explicit
dependsOn
that does not have a lifecycle task on the left-hand side is a code smell and usually a sign of not wiring outputs and inputs together properly. But having said all that, I agree to what Caleb said. test fixtures are for "things" you want to share to other projects for their tests, whether this are actual "fixtures", or data generators, or base test classes, doesn't matter. Regarding the question what changed between 5.1 and 7.4.2, nothing.
"testImplementation project("<reference to base class project>"")
also in 5.1 does not mean that Z/test can use Y/test, but only the Z/test can use Y/main. Maybe there were more things you do not mention that you changed during the upgrade and makes it not work anymore or something like that. For example look at this simple MCVE which does not compile as
ZTest
does not see `YTest`:
Copy code
./gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

./settings.gradle.kts
rootProject.name = "A"
include("Y")
include("Z")

./Y/build.gradle.kts
plugins {
    java
}

./Y/src/test/java/y/YTest.java
package y;

public class YTest {
}

./Z/build.gradle.kts
plugins {
    java
}

dependencies {
    testImplementation(project(":Y"))
}

./Z/src/test/java/z/ZTest.java
package z;

import y.YTest;

public class ZTest {
    public YTest ytest;
}
If you want to use classes from Y/test in Z/test, usually the best idea is what Caleb said, to instead put them to Y/testFixtures and depen on that from Z/test. If for whatever reason this does not work out but it must be the Y/test source set you share, then also as Caleb said, you should declare a feature variant based on the Y/test source set as per the linked documentation. This is just not there by default, as the
test
source set is meant for testing the
main
source set in the same project and not for providing test utilities for other projects, for which
testFixutres
are intended.
Btw. I strongly recommend you switch to Kotlin DSL. By now it is the default DSL, you immediately get type-safe build scripts, actually helpful error messages if you mess up the syntax, and amazingly better IDE support if you use a good IDE like IntelliJ IDEA or Android Studio. 🙂
c
I'd like to say that there are 2 scenarios I probably wouldn't use test fixtures for shared test stuff. 1. as an example, spring boot requires that you always have... an application context, which is easiest resolved by having a class with an
@SpringBootApplication
. Since this class has no specific dependencies on any of my other projects, I have simply put it in its own subproject. A test fixture should probably need to sit between main and the test suite always. Not being desirable in main, but being useful in more than one of your test suites. 2. the second thing I'd say is whether or not it's a final test class... meaning not a base class but something that junit, or likewise would actually execute by itself. That would feel like it needs refactoring or at least to live in a feature variant by another name. neither of these sound like the case here, so I'll say you're fine on that front.