How to print exception message in Kotlin - kotlin

I have defined an exception in Kotlin but when I do e.message it prints fully qualified exception name followed by message but I want to print only exception message.
public final class AbcException extends Exception {
private static final long serialVersionUID = 1L;
public AbcException() {
}
public AbcException(String message) {
super(message);
}
public AbcException(String message, Throwable cause) {
super(message, cause);
}
public AbcException(Throwable cause) {
super(cause);
}
}
I am matching asserting it in tests and want to do
assertEquals(e.message, "CaseItem delete failed: item token=token1")
But I am doing this because it prints fully qualifies exception name too
assertTrue(e.message?.contains("Case Item tokens=token2 does not exist.")!!)
Expected
"Some message"
Actual
"exceptionClass: Some message"

That's not Kotlin, it's Java.
The equivalent Kotlin exception would look something like this:
class AbcException(
message: String? = null,
cause: Throwable? = null
) : Exception(message, cause) {
// Bit odd to have an exception that wraps a cause with no message, but ok.
constructor(cause: Throwable): this(null, cause)
}
The playground test does as expected, only prints the message, excluding the exception type name:
fun main() {
try {
throw AbcException("Hello")
} catch(e: Exception) {
println(e.message)
}
}
Hello

Related

Unsuccessful API Call

I am fairly new at working with API calls in Android Studio and I'm having trouble with a block of code. It's a function that deletes a specific task item from a remote database.
Here's my code.
RepoInterface:
suspend fun deleteRemoteTask(token: String, id: Int, task: Task): Response<Task>
RepoImplementation:
override suspend fun deleteRemoteTask(token: String, id: Int, task : Task): Response<Task> = withContext(Dispatchers.IO) {
return#withContext try {
apiService.deleteTask(token, id, task)
} catch (e: Exception) {
Log.d("TAG", "deleteRemoteTask: Api Failed to run")
throw Exception(e.localizedMessage)
}
}
Repository:
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful")
}
}
I'm confident that my interface and implementation are set up correctly, but the logic in my actual repository is a bit shaky, as I keep getting an Exception thrown. The data is coming down from the View -> Viewmodel -> UseCase -> Repo -> RepoImp -> Interface -> API.
I also have
We would need a bit more info. The flow is not well implemented but no worries we all start by a point :D, I will adapt my answer to this flow.
Let's start with:
avoiding the !! non null operator in the response.body()!!.
If you mark that the response body is not null, in case it is it will crash, local.deleteTask should receive a body response nullable parameter and should handle the possible null body.
First the first; let's wrap the complete function in Repository into a try catch, something like:
try {
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful")
}
} catch(e: Exception) {
throw e
}
Let's check why that response is not successful, I see you throwing an Exception with a message in case the response is not successful, try to throw the Exception with the response.code(), something like:
try {
val response = remote.deleteRemoteTask(token, id, task)
if (response.isSuccessful) {
local.deleteTask(response.body()!!)
return local.getTaskByID(response.body()!!.id)
} else {
throw Exception("Repo: API Call unsuccessful, response error code: ${response.code()}")
}
} catch(e: Exception) {
throw e
}
In your RepoImplementation, let's print that Exception stack trace and log it:
override suspend fun deleteRemoteTask(token: String, id: Int, task : Task): Response<Task> = withContext(Dispatchers.IO) {
return#withContext try {
apiService.deleteTask(token, id, task)
} catch (e: Exception) {
Log.d("TAG", "deleteRemoteTask: Api Failed to run, stack trace: ${e.printStackTrace()}")
}
}
Then in the logcat you can search for the TAG and see what is going on with that response. Let me know how it goes :D

android, why after covert to kotlin the unit test fail

On android app, Having a java function
JSONObject addToJson(#NonNull JSONObject jsonObject, #NonNull String key, boolean value){
try {
jsonObject.put(key, value);
} catch (JSONException e) {
e.printStackTrace();
}
return jsonObject;
}
test code, it throws when call the mock jsonObject.put(key, value) and works fine:
#Test
public void test_addToJson() throws JSONException {
JSONObject jsonObject = Mockito.spy(new JSONObject());
Mockito.when(jsonObject.put(anyString(), anyBoolean())).thenThrow(new JSONException("!!! test forced exception"));
JSONObject outputObject = addToJson(jsonObject, "null", true);
assertEquals("jsonobject length should match", 0, outputObject.length());
}
after convert to kotlin
fun addToJson(jsonObject: JSONObject, key: String, value: Boolean?): JSONObject {
try {
jsonObject.put(key, value)
} catch (e: JSONException) {
e.printStackTrace()
}
return jsonObject
}
the test is failing that no exception thrown.
The Java code uses the primitive type boolean for value. The Kotlin version is using the nullable type Boolean? which seems unnecessary since the parameter could never be null in the Java version.
The change to a nullable type might cause the anyBoolean matcher to fail. You could try switching to the non-nullable type Boolean, or keep using Boolean? and change the anyBoolean matcher to anyOrNull from mockito-kotlin.

Spring Webflux: Return statuscode and message based on exception thrown

How to determine which exception is thrown and get the status code out of it in Spring Webflux.
This is the structure of my controller code.
#GetMapping("/")
fun getResults() : Mono<ResponseEntity<AccountDTO>>{
return Service.getResult()
.map {
}.doOnError {
//how to get statuscode here
throw ResponseStatusException(HttpStatus.NOT_FOUND, it.message!!)
}
Here I can get the custom message thrown, but how to get the status code? Instead of HttpStatus.NOT_FOUND. I want to capture the status code that is thrown by the service layer. Or is there a way to get the exception thrown?
I found a solution that works.
#GetMapping("/")
fun getResults() : Mono<ResponseEntity<AccountDTO>>{
return Service.getResult()
.map {
}.doOnError {
if(it is NotFoundException)
{
throw ResponseStatusException(HttpStatus.NOT_FOUND)
}
else{
throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR)
}
}

Kotlin, MockK: How to verify that a method call throws an exception with MockK?

I am learning Kotlin and MockK. I have seen how to verify that a method was called and how to check the result. But how would I check, that the method has thrown an exception?
Sorry, I found it: assertFailsWith<EceptionClass> { methodCall() }
This is my approach, hope its helping you
class TestClass {
fun testVerify() {
throw Exception("exception")
}
}
#Test
fun mockTest() {
try {
TestClass().testVerify()
}
catch (e: Exception) {
assertEquals(e.message, "exception")
}
}

What is the reason for twitter4j.StreamListner IllegalAccessError in Kotlin?

When implementing a twitter4j.StatusListner in Kotlin, I get the following IllegalAccessError and associated stack trace:
Exception in thread "main" java.lang.IllegalAccessError: tried to access class twitter4j.StreamListener from class rxkotlin.rxextensions.TwitterExampleKt$observe$1
at rxkotlin.rxextensions.TwitterExampleKt$observe$1.subscribe(TwitterExample.kt:50)
at io.reactivex.internal.operators.observable.ObservableCreate.subscribeActual(ObservableCreate.java:40)
at io.reactivex.Observable.subscribe(Observable.java:10700)
at io.reactivex.Observable.subscribe(Observable.java:10686)
at io.reactivex.Observable.subscribe(Observable.java:10615)
at rxkotlin.rxextensions.TwitterExampleKt.main(TwitterExample.kt:8)
Produced by the following code:
val twitterStream = TwitterStreamFactory().instance
// See https://stackoverflow.com/questions/37672023/how-to-create-an-instance-of-anonymous-interface-in-kotlin/37672334
twitterStream.addListener(object : StatusListener {
override fun onStatus(status: Status?) {
if (emitter.isDisposed) {
twitterStream.shutdown()
} else {
emitter.onNext(status)
}
}
override fun onException(e: Exception?) {
if (emitter.isDisposed) {
twitterStream.shutdown()
} else {
emitter.onError(e)
}
}
// Other overrides.
})
emitter.setCancellable { twitterStream::shutdown }
If I don't use Rx, it makes the exception a bit simpler:
twitterStream.addListener(object: twitter4j.StatusListener {
override fun onStatus(status: Status) { println("Status: {$status}") }
override fun onException(ex: Exception) { println("Error callback: $ex") }
// Other overrides.
})
Exception in thread "main" java.lang.IllegalAccessError: tried to access class twitter4j.StreamListener from class rxkotlin.rxextensions.TwitterExampleKt
at rxkotlin.rxextensions.TwitterExampleKt.main(TwitterExample.kt:14)
However, if I implement a Java wrapper function, no error is thrown and the behaviour is as expected:
Wrapper -
public class Twitter4JHelper {
public static void addStatusListner(TwitterStream stream, StatusListener listner) {
stream.addListener(listner);
}
}
Revised implementation -
val twitterStream = TwitterStreamFactory().instance
val listner = object: StatusListener {
override fun onStatus(status: Status?) {
if (emitter.isDisposed) {
twitterStream.shutdown()
} else {
emitter.onNext(status)
}
}
override fun onException(e: Exception?) {
if (emitter.isDisposed) {
twitterStream.shutdown()
} else {
emitter.onError(e)
}
}
// Other overrides.
}
Twitter4JHelper.addStatusListner(twitterStream, listner)
emitter.setCancellable { twitterStream::shutdown }
This revised solution comes from a blog post, which I think tries to explain the cause but Google translate is not being my friend. What is causing the IllegalAccessError? Is there a purely Kotlin based solution, or will I have to live with this workaround?
Yep that's not going to work.
addListener method takes a StreamListener param and StreamListener is non-public (package private). I would definitely raise a bug against Kotlin compiler for this.
The code Kotlin compiler generates is:
TwitterStream twitterStream = (new TwitterStreamFactory()).getInstance();
twitterStream.addListener((StreamListener)(new StatusListener() {
// ..overrides ...
}));
StatusListener already implements StreamListener so I don't see why the cast is required.
I worked around this by using a java utility class:
public class T4JCompat {
public static void addStatusListener(TwitterStream stream, StatusListener listener) {
stream.addListener(listener);
}
public static void removeStatusListener(TwitterStream stream, StatusListener listener) {
stream.removeListener(listener);
}
}
You can call these methods from Kotlin and things work as expected.