もなかアイスの試食品

「とりあえずやってみたい」そんな気持ちが先走りすぎて挫折が多い私のメモ書きみたいなものです.

Kotlinで期待していない例外UndeclaredThrowableExceptionがやってくる

はじめに

SpringBoot + Kotlinでテストを書いていた

自分で作成した例外が投げられるのを期待したテストを書いていたけど、期待していた例外が発生しなかった・・・

発生していた例外は「UndeclaredThrowableException」

Undeclared・・・未宣言、、、一体なんのこと??

SpringBoot + Kotlinでのコーディングは初めてだったのもあり解決にちょっと時間が掛かった

環境

  • SpringBoot:2.2.2
  • Kotlin:1.3.61

結論

結局、例外が発生するメソッドに「@Throws」を付ければ解決した

@Service
class CompanyService(
        private val companyRepository: CompanyRepository
) {

    @Transactional
    @Throws(NotFoundException::class)  // <---コレを追加
    fun deleteCompany(id: Int) {
        val company = companyRepository.findById(id)
                .orElseThrow {
                    NotFoundException()
                }
        companyRepository.delete(company)
        companyRepository.flush()
    }

}

実際のユニットテスト

@Test(expected = NotFoundException::class)
fun deleteCompany_NotFound_001() {
    run {
        val company = companyRepository.findById(/*存在しないデータ*/999999)
        Assert.assertFalse(company.isPresent)
    }

    target.deleteCompany(999999)
}

例外の定義

@ResponseStatus(value = HttpStatus.NOT_FOUND)
class NotFoundException: Exception() {
}

@Throwsを付けていない場合「target.deleteCompany(999999)」を呼び出すと、期待しているNotFoundExceptionではなく、UndeclaredThrowableExceptionが発生しテストが失敗する

発生したUndeclaredThrowableExceptionインスタンスの内部に、実際に投げている例外インスタンス(ここで言うNotFoundException)があった

@ResponseStatusを付与しているし、UndeclaredThrowableExceptionをハンドルせずに投げっぱなしにしたいなぁと思っていた

@Throwsがあったのを思い出して、試しに付けてみたら期待した動作になった

おわりに

Kotlinのメソッド内でJavaで言うところの検査例外を投げ、その例外が@Throwsで宣言していない場合、UndeclaredThrowableExceptionにラッパーされるっぽい

@Throwsを書かなくてもKotlinはコンパイルエラーにならないので、Spring(SpringBoot)の場合、@ControllerAdviceとか@ExceptionHandlerでUndeclaredThrowableExceptionをハンドリングするのが楽なのかもしれない(でもこの方法使えるのControllerだけか・・・?)

参考サイト

stackoverflow.com

docs.oracle.com

gsh-kz.hatenadiary.org