在 Kotlin 中测试预期异常

Test expected exceptions in Kotlin

在 Java 中,程序员可以像这样为 JUnit 测试用例指定预期的异常:

@Test(expected = ArithmeticException.class)
public void omg()
{
    int blackHole = 1 / 0;
}

我如何在 Kotlin 中执行此操作?我尝试了两种语法变体,但其中 none 有效:

import org.junit.Test

// ...

@Test(expected = ArithmeticException) fun omg()
    Please specify constructor invocation;
    classifier 'ArithmeticException' does not have a companion object

@Test(expected = ArithmeticException.class) fun omg()
                            name expected ^
                                            ^ expected ')'

您可以使用 @Test(expected = ArithmeticException::class) 或更好的 Kotlin 库方法之一,例如 failsWith().

您可以使用具体化的泛型和像这样的辅助方法使它更短:

inline fun <reified T : Throwable> failsWithX(noinline block: () -> Any) {
    kotlin.test.failsWith(javaClass<T>(), block)
}

以及使用注释的示例:

@Test(expected = ArithmeticException::class)
fun omg() {

}

Java 示例 JUnit 4.12 的 Kotlin 翻译是:

@Test(expected = ArithmeticException::class)
fun omg() {
    val blackHole = 1 / 0
}

但是,JUnit 4.13 introduced 两个 assertThrows 方法用于更细粒度的异常范围:

@Test
fun omg() {
    // ...
    assertThrows(ArithmeticException::class.java) {
        val blackHole = 1 / 0
    }
    // ...
}

两种assertThrows方法return额外断言的预期异常:

@Test
fun omg() {
    // ...
    val exception = assertThrows(ArithmeticException::class.java) {
        val blackHole = 1 / 0
    }
    assertEquals("/ by zero", exception.message)
    // ...
}

Kotlin has its own test helper package 可以帮助进行这种单元测试。

你的测试可以通过使用 assertFailWith:

@Test
fun test_arithmethic() {
    assertFailsWith<ArithmeticException> {
        omg()
    }
}

您可以使用 Kotest 来实现。

在您的测试中,您可以使用 shouldThrow 块包装任意代码:

shouldThrow<ArithmeticException> {
  // code in here that you expect to throw a ArithmeticException
}

您还可以将泛型与 kotlin.test 包一起使用:

import kotlin.test.assertFailsWith 

@Test
fun testFunction() {
    assertFailsWith<MyException> {
         // The code that will throw MyException
    }
}

JUnit5 内置了 kotlin support

import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows

class MyTests {
    @Test
    fun `division by zero -- should throw ArithmeticException`() {
        assertThrows<ArithmeticException> {  1 / 0 }
    }
}

另一个语法版本使用 kluent:

@Test
fun `should throw ArithmeticException`() {
    invoking {
        val backHole = 1 / 0
    } `should throw` ArithmeticException::class
}

断言验证异常的扩展 class 以及错误消息是否匹配。

inline fun <reified T : Exception> assertThrows(runnable: () -> Any?, message: String?) {
try {
    runnable.invoke()
} catch (e: Throwable) {
    if (e is T) {
        message?.let {
            Assert.assertEquals(it, "${e.message}")
        }
        return
    }
    Assert.fail("expected ${T::class.qualifiedName} but caught " +
            "${e::class.qualifiedName} instead")
}
Assert.fail("expected ${T::class.qualifiedName}")

}

例如:

assertThrows<IllegalStateException>({
        throw IllegalStateException("fake error message")
    }, "fake error message")

第一步是在测试注解中添加(expected = YourException::class)

@Test(expected = YourException::class)

第二步是添加这个功能

private fun throwException(): Boolean = throw YourException()

最后你会得到这样的东西:

@Test(expected = ArithmeticException::class)
fun `get query error from assets`() {
    //Given
    val error = "ArithmeticException"

    //When
    throwException()
    val result =  omg()

    //Then
    Assert.assertEquals(result, error)
}
private fun throwException(): Boolean = throw ArithmeticException()

org.junit.jupiter.api.Assertions.kt

/**
 * Example usage:
 * ```kotlin
 * val exception = assertThrows<IllegalArgumentException>("Should throw an Exception") {
 *     throw IllegalArgumentException("Talk to a duck")
 * }
 * assertEquals("Talk to a duck", exception.message)
 * ```
 * @see Assertions.assertThrows
 */
inline fun <reified T : Throwable> assertThrows(message: String, noinline executable: () -> Unit): T =
        assertThrows({ message }, executable)

没有人提到 assertFailsWith() returns 值,您可以检查异常属性:

@Test
fun `my test`() {
        val exception = assertFailsWith<MyException> {method()}
        assertThat(exception.message, equalTo("oops!"))
    }
}

这个简单的示例适用于 Junit 的 4.13.2 版本

    @Test
    fun testZeroDividing(){
        var throwing = ThrowingRunnable {  /*call your method here*/ Calculator().divide(1,0) }
        assertThrows(/*define your exception here*/ IllegalArgumentException::class.java, throwing)
    }