在 Kotlin 中测试预期的异常

Posted

技术标签:

【中文标题】在 Kotlin 中测试预期的异常【英文标题】:Test expected exceptions in Kotlin 【发布时间】:2015-07-31 15:30:18 【问题描述】:

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

@Test(expected = ArithmeticException.class)
public void omg()

    int blackHole = 1 / 0;

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

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 ')'

【问题讨论】:

【参考方案1】:

JUnit 4.12 的 Java 示例的 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 方法都返回预期的额外断言异常:

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

【讨论】:

【参考方案2】:

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

使用assertFailWith,您的测试可以非常有表现力:

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

【讨论】:

如果您的链接出现 404,kotlin.test 是否已被其他内容替换? @fredoverflow 不,不会被替换,只是从标准库中删除。我已经更新了 github kotlin 存储库的链接,但不幸的是我找不到任何文档链接。无论如何 jar 是由 intelliJ 中的 kotlin-plugin 提供的,或者您可以在网上找到它或将 maven/grandle 依赖项添加到您的项目中。 编译“org.jetbrains.kotlin:kotlin-test:$kotlin_version” @mac229 s/compile/testCompile/ @AshishSharma : kotlinlang.org/api/latest/kotlin.test/kotlin.test/…assertFailWith 返回异常,您可以使用它来编写自己的断言。【参考方案3】:

您可以使用 @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() 


【讨论】:

javaClass&lt;T&gt;() 现已弃用。请改用MyException::class.java failsWith 已弃用,应使用assertFailsWith。【参考方案4】:

您可以为此使用Kotest。

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

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

【讨论】:

似乎行它不能以正确的方式工作。我检查 1. shouldThrow someMethod().isOK shouldBe true - 绿色 2. shouldThrow someMethod().isOK shouldBe false - 绿色 someMethod() throw "java .lang.AssertionError: message" 什么时候应该出现,如果正常则返回对象。在这两种情况下,当 OK 和 NOT 时 shouldThrow 都是绿色的。 也许看看文档,自 2016 年我的回答以来,它可能已经改变了。github.com/kotlintest/kotlintest/blob/master/doc/…【参考方案5】:

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 
    

【讨论】:

这是我的首选答案。如果您在 assertThrows 上收到 Cannot inline bytecode built with JVM target 1.8 into bytecode that is being built with JVM target 1.6,请确保您的 build.gradle 有 compileTestKotlin kotlinOptions.jvmTarget = "1.8" 【参考方案6】:

您还可以在 kotlin.test 包中使用泛型:

import kotlin.test.assertFailsWith 

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

【讨论】:

【参考方案7】:

没有人提到 assertFailsWith() 返回值,你可以检查异常属性:

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

【讨论】:

【参考方案8】:

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

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")

【讨论】:

【参考方案9】:

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)

【讨论】:

【参考方案10】:

另一个版本的语法使用kluent:

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

【讨论】:

【参考方案11】:

第一步是在测试注释中添加(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()

【讨论】:

【参考方案12】:

这个简单的示例在 4.13.2 版本的 Junit 中工作

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

【讨论】:

以上是关于在 Kotlin 中测试预期的异常的主要内容,如果未能解决你的问题,请参考以下文章

深潜Kotlin协程(十五):测试 Kotlin 协程

Kotlin-multiplatform:如何执行 iOS 单元测试

称为验证的 Mockito Kotlin 单元测试方法抛出空指针异常

Kotlin 协程单元测试错误:线程“main @coroutine#1 @coroutine#2”中的异常 java.lang.NullPointerException

KotlinKotlin 与 Java 互操作 ③ ( Kotlin 中处理 Java 异常 | Java 中处理 Kotlin 异常 | @Throws 注解处理异常 | 函数类型互相操作 )

Kotlin 异常处理