The #JvmSynthetic annotation is allowed to be used on a file, but I can't figure out what the purpose of this would be.
I was hoping I could hide a file containing a bunch of Kotlin-only extension methods from Java users, but that doesn't seem to be the case:
// Extensions.kt
#file:JvmSynthetic
#JvmSynthetic
fun Foo.mySyntheticExtension() = ...
fun Foo.myExtension() = ...
// Java usage
// This doesn't compile (as expected)
Extensions.mySyntheticExtension(foo);
// This compiles fine, so #JvmSynthetic on a file does not trickle down to all its functions
Extensions.myExtension(foo);
Even without the non-synthetic method Java users still see the cluttering ExtensionsKt class, although it appears empty to them.
If #file:JvmSynthetic doesn't hide the file('s generated class) from Java, nor trickles down the synthetic status to all functions in it, what is its intended purpose?
The original proposal that caused this annotation target to be added was KT-41884:
The rationale given was:
This would apply to the synthesized class which encapsulates top-level members. This allows hiding those members from Java when they are internal visibility.
For example:
// ManyInternals.kt, in module A
#file:JvmSynthetic
internal fun foo() {
}
internal fun bar() {
}
// Main.java, in module B
public class Main {
public static void main(String[] args) {
ManyInternalsKt.foo(); // error
}
}
I'm trying to follow this tutorial https://dev.to/tagmg/step-by-step-guide-to-building-web-api-with-kotlin-and-dropwizard and am instead writing my gradle.build file in Kotlin's DSL and am finding there is no direct mapping from Groovy to Kotlin and I'm now getting this error when running ./gradlew run:
(4, 1): Duplicate JVM class name 'dropwizard/tut/AppKt' generated from: package-fragment dropwizard.tut, package-fragment dropwizard.tut
plugins {
// Apply the Kotlin JVM plugin to add support for Kotlin on the JVM.
id("org.jetbrains.kotlin.jvm").version("1.3.31")
// Apply the application plugin to add support for building a CLI application.
application
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
jcenter()
}
dependencies {
// Use the Kotlin JDK 8 standard library.
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
// Use the Kotlin test library.
testImplementation("org.jetbrains.kotlin:kotlin-test")
// Use the Kotlin JUnit integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
compile("io.dropwizard:dropwizard-core:1.3.14")
}
application {
// Define the main class for the application
mainClassName = "dropwizard.tut.AppKt"
}
tasks.withType<Jar> {
manifest {
attributes["Main-Class"] = application.mainClassName
}
from({
configurations.runtimeClasspath.get().filter { it.name.endsWith("jar") }.map { zipTree(it) }
})
}
tasks.named<JavaExec>("run") {
args("server", "config/local.yaml")
}
I cannot tell (yet) why this happens but to work around it add #file:JvmName("SomethingUnique") to your JVM file. Note that renaming the file will not help and lead to the same error. Only changing the output name will resolve it.
The JVM only knows how to load classes, so the Kotlin-to-JVM compiler generates classes to hold top-level val or fun declarations.
When you have two similarly named files
// src/commonMain/kotlin/com/example/Foo.kt
package com.example
val a = 1
and
// src/jvmMain/kotlin/com/example/Foo.kt
package com.example
val b = 2
the kotlin-to-JVM compiler generates
package com.example;
public class FooKt {
public static final int a = 1;
}
and
public com.example;
public class FooKt {
public static final int b = 2;
}
Obviously, these two files can't coexist in the same JVM ClassLoader, hence the error message.
Solutions involve:
As #Fleshgrinder noted, adding a file-level JvmName annotation to at least one to override the derived name, FooKt.
Renaming files to be different where possible.
Moving top-level val and fun declarations from those files into other files so Kotlin does not need to create the FooKt class.
Moving top-level val and fun declarations into objects or companion objects.
I try to run Kotlin instrumentation tests for android.
In my app/build.gradle:
android {
dataBinding {
enabled = true
}
compileSdkVersion 28
defaultConfig {
applicationId "com.myproject"
minSdkVersion 18
targetSdkVersion 28
versionCode 6
versionName "0.0.7"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
androidTest.java.srcDirs += 'src/androidTest/kotlin'
}
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test:rules:1.1.2-alpha02'
androidTestImplementation 'androidx.test:runner:1.1.2-alpha02'
In folder /app/src/androidTest/kotlin/com/myproject/ I has Kotlin test:
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import org.junit.Rule
import org.junit.runner.RunWith
import androidx.test.rule.ActivityTestRule
import com.myproject.ui.activity.TradersActivity
import org.junit.Before
#RunWith(AndroidJUnit4::class)
#SmallTest
class TradersActivityTest {
private lateinit var stringToBetyped: String
#get:Rule
var activityRule: ActivityTestRule<TradersActivity> = ActivityTestRule(TradersActivity::class.java)
#Before
fun initValidString() {
// Specify a valid string.
stringToBetyped = "Espresso"
}
}
but when I run test I get error:
$ adb shell am instrument -w -r -e debug false -e class 'com.myproject.TradersActivityTest' com.myproject.debug.test/androidx.test.runner.AndroidJUnitRunner
Client not ready yet..
Started running tests
java.lang.RuntimeException: Delegate runner 'androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner' for AndroidJUnit4 could not be loaded.
at androidx.test.ext.junit.runners.AndroidJUnit4.throwInitializationError(AndroidJUnit4.java:92)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:82)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:51)
at androidx.test.ext.junit.runners.AndroidJUnit4.<init>(AndroidJUnit4.java:46)
at java.lang.reflect.Constructor.newInstance(Native Method)
at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
at org.junit.internal.builders.AnnotatedBuilder.runnerForClass(AnnotatedBuilder.java:86)
at androidx.test.internal.runner.junit4.AndroidAnnotatedBuilder.runnerForClass(AndroidAnnotatedBuilder.java:63)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
at androidx.test.internal.runner.AndroidRunnerBuilder.runnerForClass(AndroidRunnerBuilder.java:153)
at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
at androidx.test.internal.runner.TestLoader.doCreateRunner(TestLoader.java:73)
at androidx.test.internal.runner.TestLoader.getRunnersFor(TestLoader.java:104)
at androidx.test.internal.runner.TestRequestBuilder.build(TestRequestBuilder.java:789)
at androidx.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:544)
at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:387)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance(Native Method)
at androidx.test.ext.junit.runners.AndroidJUnit4.loadRunner(AndroidJUnit4.java:72)
... 16 more
Caused by: org.junit.runners.model.InitializationError
at org.junit.runners.ParentRunner.validate(ParentRunner.java:418)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:43)
at androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner.<init>(AndroidJUnit4ClassRunner.java:48)
... 18 more
Tests ran to completion.
I faced this problem in kotlin , used below code and it worked
#RunWith(AndroidJUnit4::class)
#LargeTest
class MainActivityTest {
private val TAG = "MainActivityTest"
#get:Rule
val activityRule = ActivityTestRule(MainActivity::class.java)
}
follow the link below:-
https://developer.android.com/training/testing/junit-rules
The error message displayed when I delete the #Test method.
You can try to put a #Test method and run
#Test
public void signInTest() {
}
For those who still have problems like this, This happens because Kotlin compiler wants to generate getter/setter properties. for example, I've got this error because I didn't mention #Rule annotation with #get:Rule.
If we annotate properties like this with #get:... or #JvmField it could solve the problem.
#get:Rule
var mActivityRule = ActivityTestRule(MainActivity::class.java)
Or
#Rule
#JvmField
var mActivityRule = ActivityTestRule(MainActivity::class.java)
#JvmField instructs the Kotlin compiler not to generate getters/setters for this property and expose it as a field.
In regards to #RunWith(AndroidJUnit4::class)
Delete
import androidx.test.ext.junit.runners.AndroidJUnit4
Use
import androidx.test.runner.AndroidJUnit4
I got this error when I have some parameters in my test method.
So, if you have it - delete it.
#Test
fun someTestMethod(someParams: Int) { // delete method params
assert(1==1)
}
just make sure that if your test is calling any suspend methods, you run your test with runBlocking{}. Solved the problem for me
Max Kilzieh answer worked for me
Using import androidx.test.runner.AndroidJUnit4 instead of import androidx.test.ext.junit.runners.AndroidJUnit4
I just wasted all day on this issue 😵
I'm guessing you are using the multiplatform plugin like I am.
I was about to give up on trying to moving my instrumented tests to src/androidTest/kotlin instead of src/androidTest/java
when I decided to log kotlin.sourcesets.names, which printed
[androidAndroidTest, androidAndroidTestDebug, androidDebug, androidMain, androidRelease, androidTest, androidTestDebug, androidTestRelease, commonMain, commonTest, iosArm32Main, iosArm32Test, iosArm64Main, iosArm64Test, iosX64Main, iosX64Test, macosX64Main, macosX64Test]
Soo I realized in order for dependencies to resolve correctly I needed to change
androidTestImplementation -> androidAndroidTestImplementation
testImplementation -> androidTestImplementation
They don't describe this in the documentation of course.
(as far as I have found)
... so for example, for you, I think the dependencies for your instrumented test dependencies should be:
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
not
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
You don't have any functions annotated with #Test. This is likely the cause of the issue. I've run my own test using the AndroidJUnit4 runner on a class without #Test functions, and reconstructed this behavior.
The AndroidJUnit4 JUnit4 runner is the cause of this issue, and despite its attempt to provide a detailed message (see the code where the exception is thrown), it gives you a wrong message.
The following are more ways to achieve this error:
#Test
fun willThrowInitializationErrorBecauseReturnsNonUnitValue(): Int {
return 0
}
#Test
fun willThrowInitializationErrorBecauseTakesParameters(param: Int) {
}
The error is generated by the fact that you're writing JUnit4 style tests, and they're expected to conform to JUnit4 semantics, but it would be nice if the error message were more clear.
To make matters worse, if one test function is messed up the runner won't run any tests because the InitializationError is thrown in the constructor of the AndroidJUnit4 class.
In My case while using Java,I faced issue when I did below mistakes.
1)I had made ActivityTestRule as private.
All test cases should be public always.
2)I didn't add static for BeforeClass and AfterClass methods
Methods of #BeforeClass and #AfterClass should be static
Ex)#BeforeClass
public static void setUp(){
}
Also just to expand on the answers, but it won't help for this particular reason:
If you use kotlin and get this while testing, make sure you aren't using suspend in DAO.
(Sorry to put it here, there was no thread with this in kotlin and I already found the solution so it might help someone)
Let's say I am writing a library and have a class that looks something like this (contrived example, but shows self reference:
import java.util.logging.Logger
class MyClass(private val myNum: Int) {
companion object {
private val LOG = Logger.getLogger(MyClass::class.java.canonicalName)
}
constructor() : this(1337)
fun addTo(num: Int): Int {
LOG.fine { "Adding num $num to $myNum" }
return myNum + num
}
fun doubleAdd(num: Int): Int = 2 * addTo(num)
}
Now, I have decided that I want to deprecate this class and have my consumers move on to to better things, so I give them a warning.
#Deprecated("Don't use!", level = DeprecationLevel.WARNING)
class MyClass(private val myNum: Int) {
// ...
}
Now, after some more time I'd like to increase the strictness with my deprecation. I still want the library to be binary compatible, so I do not remove the code I see that there is the DeprecationLevel.ERROR available, so I try to use it.
#Deprecated("Don't use!", level = DeprecationLevel.ERROR)
class MyClass(private val myNum: Int) {
// ...
}
Except now, when I try to compile my own project, I get compiler errors:
e: /path/to/project/src/main/kotlin/MyClass.kt: (7, 44): Using 'MyClass' is an error. Don't use!
e: /path/to/project/src/main/kotlin/MyClass.kt: (10, 23): Using 'MyClass' is an error. Don't use!
This is on both the MyClass reference and the this primary constructor reference.
What is the point of DeprecationLevel.ERROR? If I am using it wrong, what is the intended use, and how do I use it?
NOTE: This whole example was done with Kotlin 1.2.21
It does exactly what is described in the documentation: DeprecationLevel
ERROR means usage of that code generates an error in the compiler. This is when you know using the code is going to cause problems and you'd rather crash the compilation, even if that code compiled fine previously.
There is also the HIDDEN deprecation level which does what you describe. It 'hides' the annotated element from the compiler but leaves it in the binary output. This will still cause a compilation error in your project because it is meant for binary compatibility, not newly compiled code.
Note: As of Kotlin beta 4, this problem is fixed.
fun test(a: List<String>) { }
The above Kotlin code generates byte code:
public static final void test(#NotNull List<? extends String> a)
{
Intrinsics.checkParameterIsNotNull(a, "a");
}
I have submitted an issue, but I need to circumvent this bug temporarily. Is there a way to mark a as invariant?
The problem has been fixed in out dev-branch and will be available within next public release.
Also there will be JvmSuppressWildcards/JvmWildcard annotations allowing to control generic signature.
As a temporary workaround you can use MutableList<String> in Kotlin declaration