【问题标题】:Using kotlin with jmockit将 kotlin 与 jmockit 一起使用
【发布时间】:2015-02-24 12:55:23
【问题描述】:

我需要一些使用 jmockit 和 kotlin 的建议。

(CUT) 这是我正在测试的 (Java) 类:

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

(j.1) 这是一个有效的 Java 测试类

@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) 但是对应的kotlin类抛出异常

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));
    }
}

例外:

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)

使用 gradle 运行时会抛出相同的异常。

(kt.2) 在 kotlin 中使用 @Mocked 语法我得到了一个不同的异常:

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));
    }
}

例外:

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 也许这与“对于模拟字段,声明类型的实例将由 JMockit 自动创建并分配给该字段,前提是它不是最终的。” (来自http://jmockit.org/tutorial/BehaviorBasedTesting.html

(kt.3) 但是,将 val 更改为 var 并使用 !!运算符导致工作测试...但这不是惯用的 kotlin 代码:

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));
    }
}

在 jmockit 中使用 kotlin 是否取得了更大的成功?

【问题讨论】:

    标签: junit jmockit kotlin


    【解决方案1】:

    我认为您无法使用 Kotlin 中的 JMockit(或大多数其他 JVM 替代语言,可能除了 Groovy 之外),无论如何都不可靠。

    原因是 1) JMockit 开发时没有考虑到这些语言,也没有使用它们进行测试; 2) 这些语言在编译成字节码时,会产生额外的或不同的结构,可能会混淆像 JMockit 这样的工具;他们通常也会插入对他们自己的内部 API 的调用,这也可能会妨碍他们。

    在实践中,替代语言倾向于开发自己的测试/模拟/等。工具,不仅适用于该语言及其运行时,还可以让您充分利用该语言的优势。

    就个人而言,尽管我可以认识到这些语言带来的许多好处(而且我特别喜欢 Kotlin),但我还是更愿意坚持使用 Java(它还在不断发展 - 请参阅 Java 8)。事实是,到目前为止,没有替代 JVM 语言已经接近 Java 的广泛使用,而且(IMO)他们永远不会。

    【讨论】:

    • >我宁愿坚持使用 Java(它会继续发展 - 请参阅 Java 8)只是想注意这在 Android 上并非如此。
    • @JaysonMinard Rogerio 是 jmockit 的作者
    【解决方案2】:

    我们做了一些实验,发现可以这样定义特殊函数:

    fun uninitialized<T>() = null as T
    

    然后像这样使用它:

    [Mocked] val dateTime : LocalDateTime = uninitialized()
    

    您也可以使用它来代替Matchers.any() 以获得相同的效果。 我们会考虑将其添加到编译器或标准库中。

    【讨论】:

    • 注解的语法已经改变,在上面的例子中[Mocked]现在是@Mocked
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2014-04-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多