gradle Integration test code can't see classes defined in unit test - unit-testing

I defined an integration test suite for our gradle build like this:
testing {
suites {
integrationTest(JvmTestSuite) {
dependencies {
implementation project
}
}
}
}
And to get my integration tests to see the test dependencies, I added these configurations:
configurations {
// This configuration allows us to define spring-devtools as available only in dev mode
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntimeOnly.extendsFrom testRuntimeOnly
}
That works except for one problem: My integration tests cannot see utility classes defined in the unit tests (src/test). I've searched for a solution but I cannot find out how to get the integration tests to have the unit tests on the classpath.
Can someone people tell me how to configure Gradle for this?
Thanks

The java-test-fixtures plugin was designed to share common code between test suites, even across project boundaries. I'd suggest to use it for your test model and utility classes. See the linked documentation on how to configure it.

Related

Byte-buddy and cglib not available in Spock test

I started playing around with Spock testing framework and my build.gradle dependencies section looks like this:
dependencies {
testCompile "org.spockframework:spock-core:1.3-groovy-2.5"
}
I have a (useless) Spock test that requires creation of a stub:
def 'A test that will fail'() {
given: ''
def random = Stub(Random)
}
When launched, the test fails with the given error:
CannotCreateMockException: Cannot create mock for class java.util.Random. Mocking of non-interface types requires a code generation library. Please put an up-to-date version of byte-buddy or cglib-nodep on the class path.
This error is mentioned in Spock documentation and it's caused by cglib or byte-buddy not being available at runtime.
Considering that spock-core's pom lists both byte-buddy and cglib as compile dependencies, why are they not retained at runtime? In other words, why do we need to explicitly add the following runtime dependency to our build.gradle file?
testRuntime "net.bytebuddy:byte-buddy:1.9.3"
Because both (byte-buddy and cglib-nodep) are marked as <optional>true</optional>, where from Gradle point of view they are as compileOnly dependencies - see: https://blog.gradle.org/introducing-compile-only-dependencies
Compile-only dependencies are distinctly different than regular
compile dependencies. They are not included on the runtime classpath
and they are non-transitive, meaning they are not included in
dependent projects.
They are optional dependencies, so you need to include them if you want that functionality
https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html

Android multi module test dependency

With Android Studio 3.0 / android_gradle_version = '3.0.1' / gradle-4.5
Let's say I have two android modules
module-base
module-a
When I want to access sources from module-base in module-a , I just need to write this in my
module-a.gradle
dependencies {
implementation project(path: ':module-base')
}
But, what if I want to access test sources from module-base in test of module-a?
Here does not work approach like above
dependencies {
testImplementation project(path: ':module-base')
}
I found lot of advices (few years old) which says something like
compileTestJava.dependsOn tasks.getByPath(':module-base:testClasses')
testCompile files(project(':module-base').sourceSets.test.output.classesDir)
or
testCompile project(':module-base).sourceSets.test.classes
But no one from mentioned works. There is always something wrong from the compiler point of view :-/
Can you someone help me how to create Android test code dependency between two modules?
In case anyone else ends up here, one way to accomplish this is to define the target module as a source set. Let's assume test-mdoule is the module we want to import stuff from, we can do it this way:
android {
sourceSets {
// non-instrumental test example
test {
java.srcDir project(":test-module").file("src/test/java")
}
// instrumental test example
androidTest {
java.srcDir project(":test-module").file("src/androidTest/java")
}
}
}
Reference: https://jonnycaley.medium.com/sharing-test-code-in-a-multi-module-android-project-7ce4de788016
Actually I find just workaround for this. Don't use test sources from module-base but use sources from test related module module-testutils which is defined like this
dependencies{
testImplementation project(':module-testutils')
}
Thus you can share common test code which will be excluded for non-testable apk.

How to include a Annotation Processor generated code to test classpath in Java/Kotlin

I have an Android project in process of migration from Java to Kotlin. In this project, I have a pure Kotlin module where I'm implementing a API Client with the following build.gradle:
apply plugin: 'kotlin'
apply plugin: 'kotlin-kapt'
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${kotlin_version}"
implementation "com.squareup.retrofit2:retrofit:${retrofit_version}"
implementation "com.squareup.retrofit2:converter-gson:${retrofit_version}"
implementation "com.google.code.gson:gson:${gson_version}"
implementation "com.squareup.okhttp3:logging-interceptor:${okhttp_version}"
implementation "io.reactivex.rxjava2:rxjava:${rx_java_version}"
implementation "io.reactivex.rxjava2:rxkotlin:${rx_kotlin_version}"
implementation "com.jakewharton.retrofit:retrofit2-rxjava2-adapter:${retrofit2_rxjava2_adapter_version}"
compileOnly "javax.annotation:jsr250-api:${jsr250_version}"
implementation "com.google.dagger:dagger:${dagger_version}"
kapt "com.google.dagger:dagger-compiler:${dagger_version}"
testImplementation "junit:junit:${junit_version}"
testImplementation "org.mockito:mockito-core:${mockito_version}"
testImplementation "org.hamcrest:hamcrest-junit:${hamcrest_version}"
testImplementation "com.squareup.okhttp3:mockwebserver:${mockwebserver_version}"
// Dependence injection
kaptTest "com.google.dagger:dagger-compiler:${dagger_version}"
}
There is an Annotation Processor dependency for unit tests:
kaptTest "com.google.dagger:dagger-compiler:${dagger_version}"
I can see the generated sources on build/generated/source/kapt/test directory, but they are not visible to the test sources, that is, is not possible to import the generated DaggerUnitTestComponent to inject dependencies, for exemple. And I'm having trouble to get it done.
This kind of thing I had already done with success on a Android project, with the help of this StackOverflow answer and the following snippet added to build.gradle, but for a pure Kotlin/Java project, it is not applicable.
android.applicationVariants.all {
def aptOutputDir = new File(buildDir, "generated/source/apt/${it.unitTestVariant.dirName}")
it.unitTestVariant.addJavaSourceFoldersToModel(aptOutputDir)
}
Is valid to say that I'm using Android Studio 3.0.1 and Kotlin 1.2.10. The sources of Kotlin library module lies on src/main/java and src/test/java.
You might want to take a look at an example of a Kotlin project that uses dagger: (here)
From what I see, the test sources that use the generated classes should compile just fine during a Gradle build, but the IDE might not pick them up correctly.
Try updating Kotlin to a newer version in the project (1.2.10 should handle this). If that does not help, try using the idea plugin as in the example above, configured for the test generated sources as follows:
apply plugin: 'idea'
idea {
module {
testSourceDirs += file('build/generated/source/kapt/test')
generatedSourceDirs += file('build/generated/source/kapt/test')
}
}

Grails test-app classpath

I'm trying to use test support classes within my tests. I want these classes to be available for all different test types.
My directory structure is as follows;
/test/functional
/test/integration
/test/unit
/test/support
I have test helper classes within the /test/support folder that I would like to be available to each of the different test types.
I'm using GGTS and I've added the support folder to the classpath. But whenever I run my integration tests running 'test-app' I get a compiler 'unable to resolve class mypackage.support.MyClass
When I run my unit tests from within GGTS the support classes are found and used. I presume this is because the integration tests run my app in its own JVM.
Is there any way of telling grails to include my support package when running any of my tests?
I don't want my test support classes to be in my application source folders.
The reason that it works for your unit tests inside the IDE is that all source folders get compiled into one directory, and that is added to your classpath along with the jars GGTS picks up from the project dependencies. This is convenient but misleading, because it doesn't take into account that Grails uses different classpaths for run-app and each of the test phases, which you see when you run the integration tests. GGTS doesn't really run the tests; it runs the same grails test-app process that you do from the commandline, and captures its output and listens for build events so it can update its JUnit view.
It's possible to add extra jar files to the classpath for tests because you can hook into an Ant event and add it to the classpath before the tests start. But the compilation process is a lot more involved and it looks like it would be rather ugly/hackish to get it working, and would likely be brittle and stop working in the future when the Grails implementation changes.
Here are some specifics about why it'd be non-trivial. I was hoping that you could call GrailsProjectTestCompiler.compileTests() for your extra directory, but you need to compile it along with the test/unit directory for unit tests and the test/integration directory for integration tests, and the compiler (GrailsProjectTestCompiler) presumes that each test phase only needs to compile that one directory. That compiler uses Gant, and each test phase has its own Grailsc subclass (org.grails.test.compiler.GrailsTestCompiler and org.grails.test.compiler.GrailsIntegrationTestCompiler) registered as taskdefs. So it should be possible to subclass them and add logic to compile both the standard directory and the shared directory, and register those as replacements, but that requires also subclassing and reworking GrailsProjectTestRunner (which instantiates GrailsProjectTestCompiler), and hooking into an event to replace the projectTestRunner field in _GrailsTest.groovy with your custom one, and at this point my brain hurts and I don't want to think about this anymore :)
So instead of all this, I'd put the code in src/groovy and src/java, but in test-specific packages that make it easy to exclude the compiled classes from your WAR files. You can do that with a grails.war.resources closure in BuildConfig.groovy, e.g.
grails.war.resources = { stagingDir ->
println '\nDeleting test classes\n'
delete(verbose: true) {
// adjust as needed to only delete test-specific classes
fileset dir: stagingDir, includes: '**/test/**/*.class'
}
println '\nFinished deleting test classes\n'
}

Can I configure Grails to see test classes in my plugin?

I have a large grails project split into several 'in place' plugins to keep things nice and modular. One of my plugins, called 'core services', contains all my domain classes, and also some nice set up work in an abstract test class (adding mock domain instances, etc) that is shared by a number of unit tests.
This is all great for unit tests that also live in the plugin, but I'd like to use that abstract test class to set up mock data in other grails projects' tests that uses that plugin. It doesn't seem that plugins' test classes are included on the classpath when running tests. Is there a way I can tell Grails to include them?:
//this abstract test class is in the core service plugin's test directory. My IDE has no problem with this import
import com.myproject.coreservices.service.AbstractGiftInstanceRelatedUnitTest
//this test class is in the project that uses the plugin
class ClaimGiftControllerTests extends AbstractGiftInstanceRelatedUnitTest {
.. my tests
}
And the output (non important parts removed):
| Error Compilation error compiling [unit] tests: startup failed:
...../test/unit/com/myproject/ClaimGiftControllerTests.groovy: 3: unable to resolve class com.myproject.coreservices.service.AbstractGiftInstanceRelatedUnitTest
# line 3, column 1.
import com.myproject.coreservices.service.AbstractGiftInstanceRelatedUnitTest
^
You can put your AbstractGiftInstanceRelatedUnitTest into the src/groovy folder of your plugin, instead of the test folder. That way you can include it in the test cases of the plugin and your other projects.