17

Is it possible to use Kotlin package functions and package properties in different sourcesets? When I try to do so, I have NoSuchMethodError thrown.


Example

I have Gradle project with Kotlin code and two sourcesets in it, main and test. In main, I have the following code in one of the files:

package ru.ifmo.ctddev.igushkin.dkvs
...
public val payloadSplitter: String = " ### "

In test I try to access payloadSplitter with the following code:

package ru.ifmo.ctddev.igushkin.dkvs
...
public class MessageTests {
    ...
    test fun testParsing() {
        ...
        checkParseAndToString("p1b 345 ${payloadSplitter} set a b c")
    }
    ...
}

And exactly in the first line where payloadSplitter is accessed, at runtime I get

java.lang.NoSuchMethodError: ru.ifmo.ctddev.igushkin.dkvs.DkvsPackage.getPayloadSplitter()Ljava/lang/String;

Other global variables and functions are also inaccessible in test with the same error.


UPD Kotlin team explained the issue and announced the fix here.

hotkey
  • 140,743
  • 39
  • 371
  • 326
  • 2
    Do you have any top level functions/properties in ru.ifmo.ctddev.igushkin.dkvs package in tests? – Natalia Selezneva May 27 '15 at 11:34
  • How do you run the tests? Could you post your gradle config? It looks like the test package is compiled against the main package but is ran without it. – Salomon BRYS May 28 '15 at 09:33
  • @SalomonBRYS, https://github.com/h0tk3y/dkvs/blob/master/build.gradle -- here it is. – hotkey May 28 '15 at 12:51
  • @NataliaUkhorskaya, yes. I have a top level property. I'll try to remove it a little bit later. – hotkey May 28 '15 at 12:58
  • @hotkey How do you run the tests ? Via an editor or via command line ? If the latter, which command ? – Salomon BRYS May 28 '15 at 13:07
  • @NataliaUkhorskaya, thanks, I removed the package-level property from `test` and it started working. I suggest you posting an answer with this solution. I would also be happy to know what is the exact reason of such a strange behaviour. – hotkey May 28 '15 at 17:49

3 Answers3

18

For properties and methods outside of classes, Kotlin creates a java class named ${packagename}Package with the properties and methods implemented as static methods and variables. With multiple source-sets, the java class will be created twice, once for each source-set. Your issue is that the test source-set "package class" is hiding the class compiled in the main source-set.

As mentioned in the comments above, avoid having any top-level properties or methods in the test source-set to prevent the Kotlin compiler from creating this package class in the test output directory.

Travis
  • 1,192
  • 1
  • 8
  • 17
  • Thanks. There's one more workaround which follows from what you said, I posted it as another answer. – hotkey Jun 01 '15 at 20:42
  • Another side note to this solution; at least in Gradle, you need to run a clean build, or the test runner will still find the generated Package class. – Mark McKenna Aug 25 '15 at 02:46
5

In addition to what was suggested earlier, I found another workaround: if you need package-level functions or properties in test just move the tests to different package, e.g. in your test sources:

 package ru.ifmo.ctddev.igushkin.dkvs.tests

and then do

 import ru.ifmo.ctddev.igushkin.dkvs.*

which is everything from your main package. This will make Kotlin compliler generate two non-conflicting Package classes, therefore both can have global members.

hotkey
  • 140,743
  • 39
  • 371
  • 326
0

I faced similar issue when testing kotlin-native module, it was fixed by adding this dependencies:

testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
user3471194
  • 62
  • 1
  • 3