如何模拟调用kotlin.system.exitProcess

时间:2018-12-19 08:52:29

标签: kotlin mockk

我想测试一些使用调用kotlin.system.exitProcess()的第三方代码的代码,在标准库中定义如下:

@kotlin.internal.InlineOnly
public inline fun exitProcess(status: Int): Nothing {
    System.exit(status)
    throw RuntimeException("System.exit returned normally, while it was supposed to halt JVM.")
}

当调用exitProcess()时,JVM停止并且无法进行进一步测试。我没有用ockock模拟对exitProcess()的调用。有可能吗?

一些其他信息:

第三方库是Clikt(https://ajalt.github.io/clikt/),这是一个用于构建命令行界面的不错的库。 Clikt应用程序解析命令行,如果失败,则退出。这可能是罕见的原因之一,其中可以调用System.exit。当然,还有更多可测试的解决方案,但是无论如何,当与第三方库一起工作时,争论在库中做得更好的方法已经过时了。

我真正要测试的是,当使用-help 或错误的参数调用时,我的应用程序会写入预期的用法消息。

我还尝试通过以下方式模拟对System.exit()的调用:     mockkStatic(“ java.lang.System”)     每{System.exit(any())} .throws(RuntimeException(“ blubb”)) 这会导致另一个问题,所有对System的调用都会被模拟,然后:

io.mockk.MockKException: every/verify {} block were run several times. Recorded calls count differ between runs
Round 1: class java.lang.System.getProperty(kotlin.ignore.old.metadata), class java.lang.System.exit(-630127373)
Round 2: class java.lang.System.exit(158522875)

很有趣,我设法用jmockit在Java中对此进行了测试,如下所示:

public class MainTestJ {
    private ByteArrayOutputStream consoleOut;

    @BeforeEach
    public void mockConsole() {
        consoleOut = new ByteArrayOutputStream();
        System.setOut(new PrintStream(consoleOut));
    }

    @Test
    public void can_mock_exit() {
        new MockUp<System>() {
            @Mock
            public void exit(int exitCode) {
                System.out.println("exit called");
            }
        };
        assertThatThrownBy(() -> {
            new Main().main(new String[] { "--help" });
        }).isInstanceOf(RuntimeException.class);
        assertThat(consoleOut.toString()).startsWith("Usage: bla bla ...");
    }
}

1 个答案:

答案 0 :(得分:1)

我尝试了一段时间以使其正常工作,但不幸的是这是不可能的。模拟此功能有几个困难;它是一个顶级函数,并返回Nothing。这些都是可以克服的,但是使功能内联是不可能的。内联的kotlin函数不会在字节码中生成方法,就像在锡盒上所说的那样。问题在于,Mockk和其他模拟库在模拟时会使用字节码指令。有关更多信息,请参阅此issue

您最好的选择是根本不尝试模拟此函数,而是模拟对正在使用的第三方库的调用。毕竟,您不应测试该第三方代码,而应仅测试自己的代码。更好的是,也许您应该寻找替代库。正如评论中已经提到的那样,第三方库不应退出该过程,而应由客户代码决定。