Android Studio 3.0 Canary 1: Kotlin tests or Java

2019-01-17 02:37发布

问题:

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"
}

回答1:

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.



回答2:

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:



回答3:

@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



回答4:

This Bug fixed in Android Studio 3.0 Canary 3