Any way to force a static method to be generated in a kotlin class?


#1

I'm trying to create a class in Kotiln which has a static method inside the bytecode so it works with JUnit's @Parameterized mechanism. Seems the bytecode doesn't get a static method - but the classes related class does.

e.g. this code…

import org.junit.* import org.junit.runners.AllTests import org.junit.runner.RunWith import org.junit.runners.Parameterized import org.junit.runners.Parameterized.Parameters import java.util.List

[RunWith(javaClass<Parameterized>())]
public class MyTest(val id: String) {

  Test public fun checkQUnitTest(): Unit {
  println(“Testing $id”)
  }

  class object {
  Parameters public fun findValues(): List<String> {
           return arrayList(“a”, “b”)
  }
  }
}

generates this bytecode:

public final class tests.MyTest extends java.lang.Object implements jet.JetObject{   final java.lang.String id;   public static tests.MyTest$ClassObject$ $classobj;   public final void checkQUnitTest();   public final java.lang.String getId();   public tests.MyTest(java.lang.String);   public static {}; } public class tests.MyTest$ClassObject$ extends java.lang.Object implements jet.JetObject{   public final java.util.List findValues();   public tests.MyTest$ClassObject$(); }

I’m trying to get MyTest to have the static method findValues() on it. Anyone any idea how to make this happen? I realise it’ll make the bytecode a little bigger; but I’d like to create a static method that delegates to the class object’s methods. So this method appears by just delegating to the $classobj instance

``

  public static java.util.List findValues();

I can imagine others hitting this too in java interop (particularly things like junit)


#2

Ah, the joys of JUnit requiring you to use static methods. James, why are you doing this to yourself? :-)


#3

You can use package-level functions


#4

I’m going to bump this because its at the top of google for ‘kotlin parameterized junit’

I didn’t try package level functions because I assumed the Parameterized JUnit driver wont look for them.

@cedricbeust thats a good point and it reveals an interesting quirk in my own thinking; I’m willing to push our project to use kotlin but I’m not willing to push off JUnit for TestNG --I guess that speaks to how good the intelliJ integration is, its a lot easier to update to kotlin than it would be for me to re-write our tests scripts to accept JUnit and TestNG.

That said, its worth mentioning, that in my case I ~solved this problem with pure kotlin.

class OPYLImporterGrammarFixture : CommonFixtureBase() {
    data class TestCase(val name : String, val document : String, val importantThing : Boolean)

    private val inputsByTestName = listOf(
        TestCase("single vertical input with minimal vals",
                    """inputs :
                    |  x1 :
                    |    lower : 1.23
                    |    upper : 2.34
                    """,
                   importantThing = true
             ),
             TestCase("single horizontal input with minimal vals",
                    """inputs :
                    |  x1 : { lower : 1.23, upper:2.34 }
                    """,
                    importantThing = false
            )
    );

    @Test fun when_parsing_document_should_parse_correctly(){

        for((name, document, important) in inputsByTestName){

            //setup
            val (parser, tokens, errorCount) = makeParserFor(document)

            //act
            val result = parser.document()

            //assert
            assertThat(errorCount()).describedAs("errorCount for $name").isEqualTo(0)
        }
    }
}

One problem is that the error messages on a system like this aren’t as nice since the tests context isn’t built into the stack like it is with a traditional test You could surround the body of the for loop in a try-catch, and add some context to the exception, but that would break some of intelliJs handling of comparison failures and etc. Still, in terms of making it look pretty, I think this is pretty spot-on.


#5

This can now be mitigated with @JvmStatic


#6

As a much nicer alternative, use https://github.com/Pragmatists/JUnitParams
No more static methods / companion object code bloat.

A full example:

@RunWith(JUnitParamsRunner::class)
class ToSentenceCaseTest {

    @Test
    @Parameters
    fun test(input: String, expected: String) {
        assertThat(
                input.toSentenceCase()
        ).isEqualTo(
                expected
        )
    }

    fun parametersForTest() = arrayOf(
            arrayOf(""              , "" ),
            arrayOf("a"             , "A"),
            arrayOf("word"          , "Word"),
            arrayOf("multiple Words", "Multiple words")
    )

}