【问题标题】:Test expected exceptions in Kotlin在 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 ')'

【问题讨论】:

    标签: unit-testing kotlin testing exception junit4


    【解决方案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)
                      }
                  

                  【讨论】:

                    猜你喜欢
                    • 2018-07-26
                    • 2019-01-23
                    • 2012-04-05
                    • 1970-01-01
                    • 2015-08-09
                    • 2011-07-31
                    • 2013-10-08
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多