UPDATE
A bug has been filed for this issue here:
https://youtrack.jetbrains.com/issue/KT-17951
UPDATE 2
The bug has been fixed in Android Studio 3.0 Canary 3
Original Post
I just started playing around with Android Studio 3.0 I enabled kotlin support from the get-go. I wrote a really simple Kotlin class in my project:
data class Wallet(val coins: Int) {
fun add(value: Int): Wallet = Wallet(coins + value)
fun substract(value: Int): Wallet = if (coins > value) Wallet(coins + value) else throw InsufficientFundsException()
}
Now I wanted to test the class, first I wrote a locally running unittest (test directory) in Kotlin:
class WalletTestKotlin {
@Throws(Exception::class)
@Test
fun add() {
Assert.assertEquals(22, Wallet(20).add(2).coins.toLong())
Assert.assertNotEquals(5, Wallet(2).add(13).coins.toLong())
}
}
It compiles and runs but with the error message:
Class not found:
"com.agentknopf.hachi.repository.model.WalletTestKotlin"Empty test
suite.
I thus re-wrote the test in Java:
public class WalletTest {
@Throws(exceptionClasses = Exception.class)
@Test
public void add() {
Assert.assertEquals(22, new Wallet(20).add(2).getCoins());
Assert.assertNotEquals(5, new Wallet(2).add(13).getCoins());
}
}
However that test failed as well - this time the Kotlin class "Wallet" couldn't be found:
java.lang.NoClassDefFoundError: com/example/repository/model/Wallet
I wonder if I am missing something ... Running a Java Test, that does not refer to Kotlin classes, but to Java classes only successfully completes.
My project build.gradle file is the default one:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.1.2-4'
repositories {
maven { url 'https://maven.google.com' }
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
The dependencies of my Module-specific build.gradle:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
//Kotlin support
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
//Testing libraries
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
testCompile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}
Workaround (for now):
Put this into your (app-level) build.gradle:
task copyTestClasses(type: Copy) {
from "build/tmp/kotlin-classes/debugUnitTest"
into "build/intermediates/classes/debug"
}
Then modify the test JUnit Run/Debug configuration in the bottom down 'before launch', there's 'Gradle-aware make' in it, +
another section, select gradle task
, select project build.gradle
file it is in, and type copyTestClasses
. Click here for screenshots of different test framework, but where the plumbing works the same way.
You may need to change/add more directory plumbing depending on your build type. The way you find those odd places is by brute search the project tree for the relevant .class files.
Note: Problem has been fixed with Android Studio 3.3 Canary
Thanks goes to @kat for showing me in the right direction. First of all - a bug had been filed for the issue mentioned in the OP.
My setup is as follows: Kotlin Tests are in the same directory as Java tests. To get both use cases to work:
- Refer to kotlin classes in Java tests
- Refer to kotlin classes in kotlin tests
First delete any other test run-configurations you may have. I then added these two gradle build tasks in my app-level build.gradle:
android {
...
task copyTestClasses(type: Copy) {
from "build/tmp/kotlin-classes/debugUnitTest"
into "build/intermediates/classes/debug"
}
task copySdkClasses(type: Copy) {
from "build/tmp/kotlin-classes/debug"
into "build/intermediates/classes/debug"
}
}
I then opened the run configuration menu via Run > Edit configurations. There on the left I deleted the top-level Android JUnit configurations.
I then clicked on Defaults > Android JUnit And edited the configuration as follows:
- Test kind = All in package
- Form mode = none
- Repeat = once
- Package = my base package like com.yourname
- Search for tests = in single module
- I left the middle section untouched (VM options etc)
- In the "Before launch" section add two entries by clicking on the plus icon:
- Run gradle task: Select your build.gradle of your app module and then in tasks enter the name copyTestClasses and click OK
- Add a second Run gradle task as before, but this time as a task name enter copySdkClasses and click OK
- Click Apply on the configuration and now run your tests
This is what it looks like in the end:
@AgentKnopf just right, but you need add tasks every new test case like this
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile("com.android.support.test.espresso:espresso-core:$espresso_version", {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile "junit:junit:$junit_version"
// kotlin
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
...............
}
// add 2 task in run config to make kotlin test working
// for junit
task copyTestClasses(type: Copy) {
from "build/tmp/kotlin-classes/debugUnitTest"
into "build/intermediates/classes/debug"
}
// for instrumented test
task copySdkClasses(type: Copy) {
from "build/tmp/kotlin-classes/debug"
into "build/intermediates/classes/debug"
}
afterEvaluate {
compileDebugUnitTestSources.dependsOn copyTestClasses
compileReleaseUnitTestSources.dependsOn copyTestClasses
compileDebugAndroidTestSources.dependsOn copySdkClasses
}
add tasks automatically for you
This Bug fixed in Android Studio 3.0 Canary 3