fun <T : Exception> expectException(kClass: KClass<T>, assert: () -> Unit) {
val exceptionName = kClass.simpleName
try {
assert()
Assert.fail("Expect to throw [$exceptionName] exception.")
} catch (e: Exception) {
if (!kClass.java.isInstance(e)) {
if (e is AssertionError) {
throw e
} else {
Assert.fail("Expect to throw [$exceptionName] exception but was [${e.javaClass.simpleName}].")
}
}
}
}
It works, but I want to make it better which can be called like this:
Generic functions can definitely have default arguments, but not in the way you’re using them. You’re saying “I want to have a function that can have any exception type as a parameter, and if I don’t specify the parameter value, it will be Exception::class”. Now suppose that you’re calling the function in this way:
expectThrows<IllegalArgumentException> { ... }
In this call, the type of the kClass parameter will be KClass<IllegalArgumentException>, and the value will be Exception::class. Exception is not a subclass of IllegalArgumentException, so the code will not be valid, and the compiler rejects it.
To fix the problem, note that you don’t actually need the function to be generic; you’re not using T in the function body at all. Therefore, you can change the function to:
fun <T : Throwable> expectThrows(kClass: KClass<T>, block: () -> Unit): ExpectThrows<T> {
val exceptionName = kClass.simpleName
try {
block()
} catch(e: Throwable) {
if (!kClass.java.isInstance(e)) {
Assert.fail("Expected an exception of type $exceptionName to be thrown, but was ${e.javaClass.simpleName}")
error("")
} else {
@Suppress("UNCHECKED_CAST")
return ExpectThrows(e as T)
}
}
Assert.fail("Expected an exception of type $exceptionName to be thrown, but was completed successfully.")
error("")
}
class ExpectThrows<out T : Throwable> internal constructor(val exception: T) {
infix fun catch(assertion: (e: T) -> Unit) {
assertion(exception)
}
}
inline fun <reified T : Exception> expectException(kClass: KClass<T> = T::class, assert: () -> Unit)
But inline function is not my expectation in some cases. Can we support “reified” feature just for the default arguments? I mean that I don’t need to declare “inline” and “reified” in this case. Because I think when compiling source codes, compiler understands what “T” is .