Why did Kotlin JSR223 Scripting stop working when I upgraded to Kotlin 1.3.30 - gradle

In a project that uses the javax.script scripting support added in 1.1 in its unit tests, upgrading the Kotlin language version from 1.3.21 to 1.3.30 caused those tests to fail with the following exception:
java.lang.NoClassDefFoundError: org/jetbrains/kotlin/scripting/compiler/plugin/ScriptingCompilerConfigurationComponentRegistrar
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.makeCompilerConfiguration(KotlinJsr223JvmLocalScriptEngine.kt:72)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.access$makeCompilerConfiguration(KotlinJsr223JvmLocalScriptEngine.kt:38)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine$replCompiler$2.invoke(KotlinJsr223JvmLocalScriptEngine.kt:49)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine$replCompiler$2.invoke(KotlinJsr223JvmLocalScriptEngine.kt:38)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.getReplCompiler(KotlinJsr223JvmLocalScriptEngine.kt)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine$localEvaluator$2.invoke(KotlinJsr223JvmLocalScriptEngine.kt:53)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine$localEvaluator$2.invoke(KotlinJsr223JvmLocalScriptEngine.kt:38)
at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.getLocalEvaluator(KotlinJsr223JvmLocalScriptEngine.kt)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.getReplEvaluator(KotlinJsr223JvmLocalScriptEngine.kt:55)
at org.jetbrains.kotlin.script.jsr223.KotlinJsr223JvmLocalScriptEngine.createState(KotlinJsr223JvmLocalScriptEngine.kt:59)
at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.createState$default(KotlinJsr223JvmScriptEngineBase.kt:46)
at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.getCurrentState(KotlinJsr223JvmScriptEngineBase.kt:53)
at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.nextCodeLine(KotlinJsr223JvmScriptEngineBase.kt:44)
at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.compileAndEval(KotlinJsr223JvmScriptEngineBase.kt:59)
at org.jetbrains.kotlin.cli.common.repl.KotlinJsr223JvmScriptEngineBase.eval(KotlinJsr223JvmScriptEngineBase.kt:31)
The relevant lines in build.gradle are:
dependencies {
// ... other stuff ...
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-script-util:$kotlin_version"
}
where ext.kotlin_version is either "1.3.21" or "1.3.30".
Why did this break, and how can I fix it?

It broke because JetBrains have refactored the scripting functionality into a plugin, and the dependencies required to successfully run Kotlin script through JSR223 have changed.
The relevant issue on the Kotlin bug tracker is KT-30972, which was closed as a duplicate of KT-30986.
The upshot is, you need to adjust the dependencies to include kotlin-scripting-compiler-embeddable.
dependencies {
// ... other stuff ...
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-compiler-embeddable:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-scripting-compiler-embeddable:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-script-util:$kotlin_version"
}

the working version nowadays is simply:
dependencies {
runtimeOnly("org.jetbrains.kotlin:kotlin-scripting-jsr223:${Deps.JetBrains.Kotlin.VERSION}")
}
which pulls in all necessary dependencies transitively.
Also the META-INF/services/javax.script.ScriptEngineFactory File seems not to be necessary if doing so.

Related

Avoid duplicating dependencies in a Gradle testing suite?

If I define a new Gradle testing suite as per the doco, I have to manually duplicate dependencies from the main project into the test suite.
The version catalogue stuff can help eliminate the hardcoded version numbers.
But how can I just say "give dev the same dependencies as test"?
dependencies{
// don't really care, whever version Gradle is using will do
implementation localGroovy()
// look in settings.gradle for version catalog specs
implementation libs.jooq
runtimeOnly libs.pgjdbc
jooqGenerator libs.pgjdbc
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
}
testing{
suites{
/* "dev" code/sql, etc. is "scratch" stuff that is committed, but is not
production code, not real tests. Just WIP/temporary stuff, where the
usual standards don't apply (but still - no credentials!) */
dev(JvmTestSuite){
testType = TestSuiteType.INTEGRATION_TEST
dependencies{
implementation project
// doesn't work: No signature of method: build_999.testing() is applicable for argument types
// implementation localGroovy()
// copy/pasted from dependencies block - yuck :(
implementation 'org.codehaus.groovy:groovy-all:3.0.9'
implementation libs.jooq
runtimeOnly libs.pgjdbc
}
}
}
}
dev{
useJUnitPlatform()
systemProperties defaultSysProps
}
Gradle 7.4.2

Starting with IntelliJ and Gradle : how to do jUnit tests?

Good evening,
because I want to initiate myself to LibGDX, I recently gave a try to IntelliJ Idea IDE and Gradle instead of my old Eclipse-Maven habits.
I have to recognize that such a change is not easy because I really don't find anything.
To start learning I created a project with a simple Pojo and also a unit test class.
I have no error in the editor, both Pojo and jUnit seem OK, but when I launch the unit test, I get such errors :
Can someone help me understand what's going wrong ?
EDIT : build.gradle file content :
plugins {
id 'java'
}
group 'com.citizenweb.training'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
// https://mvnrepository.com/artifact/org.projectlombok/lombok
compile group: 'org.projectlombok', name: 'lombok', version: '1.18.16'
}
Thanx by advance.
It seems you did not configure lombok dependencies properly: your test classes cannot see lombok-generated stuff (getters, setters, build). Lombok is based on annotation processor so you need to declare following dependencies in your build.gradle :
ext {
lombokVersion = "1.18.6"
}
dependencies {
// Lombok
compileOnly ("org.projectlombok:lombok:${lombokVersion}")
annotationProcessor ("org.projectlombok:lombok:${lombokVersion}")
// to make lombok available for test classes
testCompileOnly ("org.projectlombok:lombok:${lombokVersion}")
testAnnotationProcessor ("org.projectlombok:lombok:${lombokVersion}")
testImplementation("junit:junit:4.12")
}

java.lang.NoSuchMethodError: org.mockito.MockingDetails.getMockCreationSettings() - Gradle links same class twice for Mockito jar

I have configured the following dependencies in the build.gradle file.
testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.mockito:mockito-all:1.10.19"
And when running the tests getting the following error stack trace.
java.lang.NoSuchMethodError: org.mockito.MockingDetails.getMockCreationSettings()Lorg/mockito/mock/MockCreationSettings;
at org.springframework.boot.test.mock.mockito.MockReset.get(MockReset.java:107)
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:81)
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.resetMocks(ResetMocksTestExecutionListener.java:69)
at org.springframework.boot.test.mock.mockito.ResetMocksTestExecutionListener.beforeTestMethod(ResetMocksTestExecutionListener.java:56)
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:289)
And I've tried to debug it. Found something weird in the IntelliJ linked jar files. Almost most of the classes in the Mockito jar displayed twice.
Refreshing gradle dependencies or cache clear both did not work.
After so many days, I figured out that the issue is with the mockito jar version mismatch.
Changing the version from mockito-all to mockito-core build.gradle to
testCompile "org.mockito:mockito-core:2.24.0"
testCompile ("org.springframework.boot:spring-boot-starter-test") {
exclude group: "org.mockito", module: "mockito-all"
}
fixed my problem.
Thanks

Custom configuration in gradle

I'm defining separate configuration in gradle for jupiter (Junit 5) tests. I have following snippet in the gradle file:
My understanding is jupiterCompile will extend dependencies from testCompile including any dependencies defined for self. In this case my testCompile configuration doesn't have any jupiter dependencies but as jupiterCompile has those sets of dependencies, I expect them to be available. However, if I write testCompile.extendsFrom jupiterCompile it does work which is confusing to me.
configurations {
jupiterTestCompile.extendsFrom testCompile
jupiterTestRuntime.extendsFrom testRuntime
jupiterTestCompileOnly.extendsFrom testCompileOnly
}
dependencies {
def jUnitVersion = "5.5.1"
jupiterTestCompile("org.junit.jupiter:junit-jupiter-
api:${jUnitVersion}")
jupiterTestRuntime("org.junit.jupiter:junit-jupiter-
engine:${jUnitVersion}")
jupiterTestCompileOnly("org.junit.jupiter:junit-jupiter-
migrationsupport:${jUnitVersion}")
jupiterTestRuntime("org.junit.vintage:junit-vintage-
engine:${jUnitVersion}")
}
If I have common dependencies defined under testCompile and want jupiterCompile configuration to inherit them, what should be the right configuration?

Gradle Custom Plugin: gradleApi() vs Explicit Dependency

I'm developing a custom gradle plugin and the dependencies for my plugin project look like this:
dependencies {
compile gradleApi()
compile localGroovy()
compile('com.xxx.oozie:oozie-dsl-parser:1.0.127') {
exclude module: 'groovy-all'
}
testCompile('org.spockframework:spock-core:1.0-groovy-2.3') {
exclude module: 'groovy-all'
}
}
However, in the interest of reproducible builds, I'm wondering if using localGroovy() and gradleApi() is advisable.
After much googling, although I could replace localGroovy() with a specific version of groovy, I can't seem to find a definitive answer on what I would replace gradleApi() with.
Do you guys have any suggestions?
Thanks!
I suggest applying the java-gradle-plugin. It adds the gradleApi() dependency automatically and also includes some other boilerplate configurations: https://docs.gradle.org/current/userguide/javaGradle_plugin.html#gsc.tab=0
The version of the gradleApi() that is added as dependency depends on the Gradle version that you are using the build the project. For example if your wrapper has Gradle 2.14.1 the used Gradle API will be of that version.
You also do not have to worry about localGroovy() because it is already included in the gradleTestKit() dependency which is added by the plugin: https://docs.gradle.org/current/userguide/test_kit.html#sub:test-kit-automatic-classpath-injection&gsc.tab=0
Here is an example:
apply plugin: 'groovy'
apply plugin: 'java-gradle-plugin'
dependencies {
testCompile('org.spockframework:spock-core:1.0-groovy-2.4') {
exclude module: 'groovy-all'
}
}
Looking at https://github.com/gradle/gradle/issues/1835 it seems like there is no explicit dependency you can use for that purpose.
Although not equivalent to gradleApi(), if you are developing for Android you might be interested in the com.android.tools.build:gradle-api:3.3.2 dependency.

Resources