Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using kotlin with jmockit

I need some advice using jmockit with kotlin.

(CUT) This is my (Java) class under test:

public final class NutritionalConsultant {
    public static boolean isLunchTime() {
        int hour = LocalDateTime.now().getHour();
        return hour >= 12 && hour <= 14;
    }
}

(j.1) This is a working Java test class

@RunWith(JMockit.class)
public class NutritionalConsultantTest {
    @Test
    public void shouldReturnTrueFor12h(@Mocked final LocalDateTime dateTime) {
        new Expectations() {{
            LocalDateTime.now(); result = dateTime;
            dateTime.getHour(); result = 12;
        }};
        boolean isLunchTime = NutritionalConsultant.isLunchTime();
        assertThat(isLunchTime, is(true));
    }
}

(kt.1) However, the corresponding kotlin class throws an exception

RunWith(javaClass<JMockit>())
public class NutritionalConsultantKt1Test {

    Test
    public fun shouldReturnTrueFor12h(Mocked dateTime : LocalDateTime) {
        object : Expectations() {{
            LocalDateTime.now(); result = dateTime;
            dateTime.getHour(); result = 12;
        }}
        val isLunchTime = NutritionalConsultant.isLunchTime()
        assertThat(isLunchTime, eq(true));
    }
}

Exception:

java.lang.Exception: Method shouldReturnTrueFor12h should have no parameters
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:408)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:41)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

The same exception is thrown when run with gradle.

(kt.2) Using the @Mocked syntax with kotlin I am getting a different exception:

RunWith(javaClass<JMockit>())
public class NutritionalConsultantKt2Test {
    Mocked
    var dateTime : LocalDateTime by Delegates.notNull()

    Test
    public fun shouldReturnTrueFor12h() {
        object : Expectations() {{
            LocalDateTime.now(); result = dateTime;
            dateTime.getHour(); result = 12;
        }}
        val isLunchTime = NutritionalConsultant.isLunchTime()
        assertThat(isLunchTime, eq(true));
    }
}

Exception:

java.lang.IllegalArgumentException: Final mock field "dateTime$delegate" must be of a class type
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:74)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

edit 20150224 maybe this is related to "For a mock field, an instance of the declared type will be automatically created by JMockit and assigned to the field, provided it's not final." (from http://jmockit.org/tutorial/BehaviorBasedTesting.html)

(kt.3) However, changing the val to var and using a !! operator leads to a working test... but this is not idiomatic kotlin code:

RunWith(javaClass<JMockit>())
public class NutritionalConsultantKt3Test {
    Mocked
    var dateTime : LocalDateTime? = null

    Test
    public fun shouldReturnTrueFor12h() {
        object : Expectations() {{
            LocalDateTime.now(); result = dateTime;
            dateTime!!.getHour(); result = 12;
        }}
        val isLunchTime = NutritionalConsultant.isLunchTime()
        assertThat(isLunchTime, eq(true));
    }
}

Did you have more success using kotlin with jmockit?

like image 413
sk_dev Avatar asked Feb 24 '15 12:02

sk_dev


1 Answers

I don't think you'll be able to use JMockit from Kotlin (or most other JVM alternative languages, with the possible exception of Groovy), not reliably anyway.

The reasons are that 1) JMockit was not developed with such languages in mind, and isn't tested with them; and 2) these languages, when compiled to bytecode, produce additional or different constructs that may confuse a tool like JMockit; also they usually insert calls to their own internal APIs which may also get in the way.

In practice, alternative languages tend to develop their own testing/mocking/etc. tools, that not only work well for that language and its runtime, but also let you take full advantage of the language's strenghts.

Personally, even though I can recognize the many benefits such languages bring (and I particularly like Kotlin), I would rather stick with Java (which continues to evolve - see Java 8). The fact is, so far no alternative JVM language has come even close of Java's widespread use, and (IMO) they never will.

like image 165
Rogério Avatar answered Nov 02 '22 23:11

Rogério