As per kotest docs: https://github.com/kotest/kotest/blob/master/doc/nondeterministic.md
You can tell eventually to ignore specific exceptions and any others will immediately fail the test.
I want to pass multiple exceptions to eventually that I know would be thrown by my block so that I can explicitly skip them.
Right now I only see a way to pass one, how do I pass more than one exception to eventually to skip it in case the block throws those exceptions?
You may use superclass for all your exceptions like
eventually(200.milliseconds, exceptionClass = RuntimeException::class) {
throw IllegalStateException()
}
or wrap exceptions
eventually(200.milliseconds, exceptionClass = IllegalStateException::class) {
runCatching { throw UnknownError() }
.onFailure { throw IllegalStateException(it) }
}
In 4.4.3 there are no features with collection of Exception
Related
I need to collect only the first value from two emitted by flow.
I have a function that returns flow:
fun myFlow = flow {
try {
emit(localDataSource.fetchData())
} catch(e: Exception) {
// just skip this error
}
emit(remoteDataSource.fetchData(1000, 0))
}
In one special case I need only first emitted value, doesn't matter is it from local cache or remote source.
I tried this one:
fun getRandomFavoriteItem() = myFlow.first().filter { it.score > 7 }.randomOrNull()
But first() invocation always throws
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value.
What I've tried:
single() -
java.lang.IllegalArgumentException: Flow has more than one element
take(1).first() -
java.lang.IllegalStateException: Flow exception transparency is violated:
Previous 'emit' call has thrown exception kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted, no more elements needed, but then emission attempt of value
Catch error but it doesn't stop here:
myFlow.catch { e ->
if (e !is IllegalArgumentException) {
throw e
}
}.first().filter { it.score > 7 }.randomOrNull()
My questions are:
What is the point of usage first() if it doesn't work in case of more than 1 emitted values? If I would know that my flow produces only one value I could just use any other terminal operator.
How to avoid those errors and how to collect only first value without adding repeated code?
This isn't an error in first(). It's an error in your flow. You are not permitted to swallow all exceptions in a Flow in the way you have.
Some varying approaches may differ in whether they detect that error, but what you must fix is how you "just skip" all exceptions. Consider catching only the specific exceptions you're concerned about, or at least making sure to catch and rethrow CancellationException or its subclasses.
Lous Wasserman already found the problem, here some more details.
As mentioned in the error message you're also catching the AbortFlowException.
java.lang.IllegalStateException: Flow exception transparency is
violated: Previous 'emit' call has thrown exception
kotlinx.coroutines.flow.internal.AbortFlowException: Flow was aborted,
no more elements needed, but then emission attempt of value.
You're bascically catching an exception which interferes with the way flows work. The problem is not about the first function.
Since AbortFlowException is internal you cannot access it, but you can access its superclass CancellationException. You need to modify your catch block like this:
try {
emit(localDataSource.fetchData())
} catch (e: Exception) {
if(e is CancellationException) {
throw e
}
}
Now first will work in the way you expect it to.
Edit:
A better solution would be to handle the exception within fetchData (you might return null in case one was thrown). This way you don't get in the way of the flow mechanics.
If that is not possible, you could create a wrapper function which takes care of the exception handling.
I have a dilemma about handling exceptions in kotlin and coroutines. Will be thankful for any articles or your personal experience.
https://kotlinlang.org/docs/exceptions.html#checked-exceptions kotlin documentation says that exceptions were mistake so there are no checked exceptions in kotlin.
If you have multilayer architecture it is pain in the ass to handle an exception from bottom layer at the top one, cause there are no warnings or compile level checks for what exception you should wait for.
As an option you can catch exception asap, wrap it with some Result class and propagate it back as a return value.
return try {
Result.Success(api.call())
} catch(ex: IOException) {
Result.NetworkError
}
This solution works well until you get hands on coroutines with async/await.
If an exception is thrown inside the first async then whole scope becomes dead.
coroutineScope {
val r1 = async { }
val r2 = async { }
r1.await()
r2.await()
}
But if you use solution with return value, then both async will complete. Even if one of them completed with wrapped error. In 99% cases this behavior doesn't make sense.
So I run into situation interesting situation - there are no compile level/lint checks for catching/throwing checked exceptions so I can't use exceptions properly. But if I get rid of exceptions coroutines start act weird.
I want to run cleanup code after a certain block of code completes, regardless of exceptions. This is not a closeable resource and I cannot use try-with-resources (or Kotlin's use).
In Java, I could do the following:
try {
// ... Run some code
} catch(Exception ex) {
// ... Handle exception
} finally {
// ... Cleanup code
}
Is the following Kotlin code equivalent?
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.onFailure {
// ... Handle exception
}
Edit: added boilerplate exception handling - my concern is with ensuring the cleanup code runs, and maintainability.
There is one important difference, where the code inside runCatching contains an early return. A finally block will be executed even after a return, whereas also has no such magic.
This code, when run, will print nothing:
fun test1()
runCatching {
return
}.also {
println("test1")
}
}
This code, when run, will print "test2":
fun test2() {
try {
return
} finally {
println("test2")
}
}
There is one big difference between both code samples. try...finally propagates exceptions while runCatching().also() catches/consumes them. To make it similar you would have to throw the result at the end:
runCatching {
// ... Run some code
}.also {
// ... Cleanup code
}.getOrThrow()
But still, it is not really 1:1 equivalent. It catches all exceptions just to rethrow them. For this reason, it is probably less performant than simple try...finally.
Also, I think this is less clear for the reader. try...finally is a standard way of dealing with exceptions. By using runCatching() just to immediately rethrow, you actually confuse people reading this code later.
Your question sounded a little like you believed Kotlin does not have try...finally and you need to search for alternatives. If this is the case, then of course Kotlin has try...finally and I think you should use it instead of runCatching().
As per Kotlin's doc for runCatching:
Calls the specified function block and returns its encapsulated result if invocation was successful, catching any Throwable exception that was thrown from the block function execution and encapsulating it as a failure.
Even if finally always runs after a try block and also always runs after a runCatching, they do not serve the same purpose.
finally doesn't receive any argument and cannot operate on the values of the try block, while also receives the Result of the runCatching block.
TLDR; .runCatching{}.also{} is a more advanced try{}finally{}
There is also a difference in what is the result of evaluating the expression.
Consider the following code:
fun main() {
val foo = try {
throw Exception("try")
} catch(e: Exception) {
"catch"
} finally {
"finally"
}
val bar = runCatching{
throw Exception("runCatching")
}.also{
"also"
}.onFailure {
"onFailure"
}
println(foo)
println(bar)
}
The output will be:
catch
Failure(java.lang.Exception: runCatching)
https://pl.kotl.in/a0aByS5l1
EDIT:
An interesting article that points out some differences as well:
https://medium.com/#mattia23r/a-take-on-functional-error-handling-in-kotlin-515b67b4212b
Now let’s give a second look at the implementation of runCatching in the gist above. What does it do? It catches everything.
In this case, it goes even further: it catches all Throwables. For those not knowing, Throwable is everything that can go after a throw keyword; it has two descendants: Exceptions and Errors. We haven’t mentioned Errors so far; Errors usually represent something wrong that happened at a lower level than your business logic, something that can’t usually be recovered with a simple catch.
I'm working on a kotlin web backend and have something like this:
try {
val uuid = UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}
doSomething(uuid)
The code above doesn't compile since uuid is unresolved outside the try block.
Alternatives I can imagine are:
move doSomething(uuid) inside the try block, but I'd rather avoid that so I don't accidentally catch some other potential IllegalArgumentException thrown by doSomething (if that happens for whatever reason I want things to fail and get a 500 in my logs so I can investigate)
use a nullable var instead and initialize it to null but that seems a bit ugly?
This throw BadRequestException pattern is working well otherwise so I don't want to change the return type of the method or something like that in order to avoid throwing.
Is there a better / more elegant / recommended pattern for this in Kotlin?
In Kotlin, try/catch can be used as an expression. Branches that throw don't affect the resolved type. So you can write:
val uuid = try {
UUID.fromString(someString)
} catch (e: IllegalArgumentException) {
throw BadRequestException("invalid UUID")
}
I'm trying to use the inline function use with a FileInputStream instead of the classic try/catch IOException so that
try {
val is = FileInputStream(file)
// file handling...
}
catch (e: IOException) {
e.printStackTrace()
}
becomes
FileInputStream(file).use { fis ->
// do stuff with file
}
My question is, why use the function use if it stills throws exception? Do I have to wrap use in a try/catch? This seems ridiculous.
From Kotlin documentation:
Executes the given block function on this resource and then closes it
down correctly whether an exception is thrown or not.
When you use an object that implements the Closeable interface, you need to call the close() method when you are done with it, so it releases any system resources associated with the object.
You need to be careful and close it even when an exception is thrown. In this kind of situation that is error prone, cause you might not know or forget to handle it properly, it is better to automate this pattern. That's exactly what the use function does.
Your try-catch does not close the resource so you are comparing apples to oranges. If you close the resource in finally block:
val is = FileInputStream(file)
try {
...
}
catch (e: IOException) {
...
}
finally {
is.close()
}
is definitely more verbose than use which handles closing the resource.