LruCache in unit test return null - android

It's a bit wired when init Lrucache, it throws NullPointerException.
The code is:
#Test
fun testLruCache() {
try {
val code = "code"
val cache: LruCache<String, Int> = LruCache(1000)
println(cache)
Assert.assertNotNull(cache)
cache.put(code, 1)
val getValue = cache.get(code)
Assert.assertEquals(1, getValue)
println("try end")
} catch (e: Exception) {
println(e)
println("catch end")
}
}
And the output is:
java.lang.NullPointerException
catch end
The line println(cache) even without called, the LruCache throw the exception. So does the behavior in unit test is different?

Related

Should I test remoteDataSource implementation?

I have an implementation of a remoteDataSource which loooks similar to this:
class MyRemoteDataSource #Inject constructor(private val myApi: myApi) :
RemoteDataSource {
private val someErrorOccurredTryAgain= "Some error occurred. Try again later";
override suspend fun someMethod(url: String): StateFlow<Result<MyClass>> {
val result: StateFlow<Result<MyClass>> = try {
val myClass = myApi.service.someMethod(url).toMyClass()
MutableStateFlow(Result.Success((myClass)))
} catch (socketTimeoutException: SocketTimeoutException) {
MutableStateFlow(Result.Error(Exception("Connectivity issues")))
} catch (httpException: HttpException) {
var exception = Exception(someErrorOccurredTryAgain)
val errorCodeTag = "error_code"
if (httpException.response() != null) {
val errorJson = JSONObject(httpException.response()?.errorBody().toString())
if (errorJson.has(errorCodeTag)) {
val code = errorJson.getInt(errorCodeTag)
exception = when (code) {
2 -> Exception("Some type of error 2")
3 -> Exception("Some type of error 3")
else -> {
Exception("Some error occurred")
}
}
}
}
MutableStateFlow(Result.Error(exception))
} catch (exception: Exception) {
MutableStateFlow(Result.Error(Exception(someErrorOccurredTryAgain)))
}
return result
}
}
And I'm facing three doubts:
1- I'd like to test what happens when receiving a SocketTimeoutException, but when I try to mock an exception being thrown with mockito, I get an error like:
Checked exception is invalid for this method!
Invalid: java.net.SocketTimeoutException
org.mockito.exceptions.base.MockitoException:
Checked exception is invalid for this method!
Invalid: java.net.SocketTimeoutException
is it a good practice to test this kind of things?
2 - Does it makes sense to test some errors that an specific API might return (it's the case of HttpException, where there are some error codes in the errorBody
3 - In order to be able to manage properly exceptions from example from a Repository, is it better to manage the error handling as I did with some class like the following:
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
override fun toString(): String {
return when (this) {
is Success<*> -> "Success[data=$data]"
is Error -> "Error[exception=$exception]"
}
}
}
val Result<*>.succeeded
get() = this is Result.Success && data != null
or is it better to trhow a custom class of exception with specifics types like ConnectivityError or WrongRequest (based on the API response)

Mockito Test: verify static method was called inside object class

// writing junit ExerciseMainLogger class
so how to verify AnalyticsLog.insertEventLog(builder) using Mockito
I have mocked AnalyticsLog class but getting error Actually, there were zero interactions with this mock.
Wanted but not invoked
object ExerciseMainLogger {
fun setLog(eventName: String, screenId: String = "", dimension: Map<String, String> = mapOf()) {
LOG.d(TAG, "setLog - $eventName, $screenId, $dimension")
val builder = LogBuilders.EventBuilder()
.setEventName(eventName)
.setEventType(LogBuilders.EventType.NORMAL)
if (screenId.isNotEmpty()) {
builder.setScreenView(screenId)
}
if (dimension.isNotEmpty()) {
builder.setDimension(dimension)
}
AnalyticsLog.insertEventLog(builder)
}
}
AnalyticsLog Class
object AnalyticsLog {
#JvmStatic
fun insertEventLog(eventBuilder: EventBuilder) {
if (TestConfig.isTestMode()) {
LOG.d(TAG, "[SA] test mode")
return
}
try {
val eventLogs = eventBuilder.build()
val eventId = eventLogs[EVENT_ID_PROPERTY]
val result = insertLog(eventLogs)
if (FeatureManager.getInstance().getBooleanValue(FeatureList.Key.COMMON_DEVELOPER_MODE)) {
LOG.d(TAG, "[SA-DEV] insertEventLog: EventId: $eventId, logs: $eventLogs, send result: $result")
} else {
LOG.d(TAG, "[SA] insertEventLog: EventId: $eventId, send result: $result")
}
} catch (e: Exception) {
LOG.w(TAG, "[SA] insertEventLog exception, " + e.message)
e.printStackTrace()
}
}
}
My Test method looks like below. I trying to run the test case with the JUnit but getting error
mockkStatic(SamsungAnalyticsLog::class)
every { SamsungAnalyticsLog.insertEventLog(builder) } just runs
ExerciseMainLogger.setLog(
ExerciseMainLogger.EX2012,
screenId = screenId,
dimension = dimension
)
verify(exactly = 1) { AnalyticsLog.insertEventLog(builder) }
Verification failed: call 1 of 1: class com.samsung.android.wear.shealth.base.log.SamsungAnalyticsLog.insertEventLog(eq(com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#71a04ac6))). Only one matching call to SamsungAnalyticsLog(static SamsungAnalyticsLog)/insertEventLog(EventBuilder) happened, but arguments are not matching:
[0]: argument: com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#7b05129b, matcher: eq(com.samsung.context.sdk.samsunganalytics.LogBuilders$EventBuilder#71a04ac6), result: -
Stack trace:

Kotlin: try catch in CoroutineScope still crash the app

I'm using the code below for a network request throught okhttp3:
runOnDefaultDispatcher {
try {
val newsResponse = Xxxx.xxxxClient.getNews()
if (newsResponse.success && newsResponse.data != null && newsResponse.data.count() > 0) {
runOnMainDispatcher {
val adapter = NewsAdapter(newsResponse.data, getString(R.string.news)).also {
initListener(it, newsResponse.data)
}
binding.list.adapter = adapter
adapter.notifyDataSetChanged()
}
}
} catch (exception: Exception) {
runOnMainDispatcher {
binding.list.visibility = View.GONE
val errorLayout = view.findViewById<RelativeLayout>(R.id.error_layout)
errorLayout.visibility = View.VISIBLE
errorLayout.findViewById<TextView>(R.id.error_title).text = "Oops..."
errorLayout.findViewById<TextView>(R.id.error_message).text = exception.message
}
}
}
The implementation code of runOnDefaultDispatcher and runOnMainDispatcher is down below:
import kotlinx.coroutines.*
fun block(block: suspend CoroutineScope.() -> Unit): suspend CoroutineScope.() -> Unit {
return block
}
fun runOnDefaultDispatcher(block: suspend CoroutineScope.() -> Unit) =
GlobalScope.launch(Dispatchers.Default, block = block)
suspend fun <T> onDefaultDispatcher(block: suspend CoroutineScope.() -> T) =
withContext(Dispatchers.Default, block = block)
fun runOnIoDispatcher(block: suspend CoroutineScope.() -> Unit) =
GlobalScope.launch(Dispatchers.IO, block = block)
suspend fun <T> onIoDispatcher(block: suspend CoroutineScope.() -> T) =
withContext(Dispatchers.IO, block = block)
fun runOnMainDispatcher(block: suspend CoroutineScope.() -> Unit) =
GlobalScope.launch(Dispatchers.Main.immediate, block = block)
suspend fun <T> onMainDispatcher(block: suspend CoroutineScope.() -> T) =
withContext(Dispatchers.Main.immediate, block = block)
I except the exception would be caught and no crash would appear.
However the application still CRASH:
FATAL EXCEPTION: DefaultDispatcher-worker-2
Java.net.SocketException: Connection reset
The calls to launch don't work well with try/catch.
e.g. this will crash the app
try {
GlobalScope.launch { throw Excepton() }
} catch (e: Exception) {
}
On the other hand, suspend functions work with try/catch as you would expect so this example DOES NOT crash the app:
suspend fun bang(): Unit = throw Exception()
try {
bang()
} catch (e: Exception) {
}
In your code you have launch inside try/catch, meaning you have a scenario like the first example here.
The solution is to build your program as suspend functions, and only use launch one to execute the result (note: this doesn't apply universally but does apply in this scenario).
When running the program you probably want to use lifecycleScope.
Also you might want to consider using a ViewModel so that the network call survives configuration changes.
You can check the Kotlin Coroutines on Android guide for more.
You can see the difference between to code blocks. The first one will crash because exception occurs in a different thread. The second one will not crash because you are catching exception in the right place.
fun main() {
val scope = CoroutineScope(Job() + Dispatchers.Default)
//First way
try {
scope.launch {
exceptionThrowingFunction()
}
} catch (e: Exception) {
println(e.message)
}
//Second way
scope.launch {
try {
exceptionThrowingFunction()
} catch (e: Exception) {
println(e.message)
}
}
Thread.sleep(10000)
}
private suspend fun exceptionThrowingFunction() {
delay(10)
throw IllegalArgumentException("Test Error")
}

Android studio kotlin function throw exeption

Im developing an app using kotlin and MVVM architecture.I have the three layers activity,viewModel and repository, in repository i have renameDirectory() function it does some network calling to rename a directory, the function can throw an exception if the network response returns an error the problem is that the catch block in the activity layer does not catch the exception.
renameDirectory in repository
suspend fun renameDirectory(token : String,directory: Directory) {
val resp = maApi.renameDirectory("jwt $token",directory)
if(resp.isSuccessful)
return
val gson = Gson()
val type = object : TypeToken<ErrorResponse>() {}.type
val errorResponse =
gson.fromJson<ErrorResponse>(resp.errorBody()!!.charStream(), type)
throw Exception(errorResponse.error)
}
code in viewModel that calls the function
suspend fun renameDirectory(directory: Directory){
viewModelScope.launch (Dispatchers.IO){
directoriesRepository.renameDirectory(userResp!!.token!!,directory)
}
}
code in activity to calls the function and handle exceptions
try {
viewModel.renameDirectory(directory)
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar("Directory has been renamed successfully")
currentFragment.clearSelection()
}
} catch (ex: IOException) {
Log.d("IO Exception=>", ex.toString())
} catch (ex: HttpException) {
Log.d("Http Exception=>", ex.message())
} catch (ex: Exception) {
this.cancel()
withContext(Dispatchers.Main) {
horizontalProgress.toggle()
activityView.snackBar(ex.message!!)
}
}
when renameDirectory in repository calls throw Exception() the app stops,why the code in activity does not handle the exception?

How to get exception in init block kotlin

following the code, in init function I create a Person object,and have an exception,now I want to stop the progress in catch like java return. How can I do it?
class Person {
val age: String = "10"
private lateinit var person: Person
init {
try {
person = get(2)
} catch (exception: Throwable) {
}
println("----------------do it $person.age")
}
fun get(i: Int): Person {
when (i) {
1 -> {
return Person()
}
else -> {
throw MyException("aaaaaaaaa")
}
}
}
}
If an instance cannot be created due to errors in init, this error shouldn't be suppressed but delegated to the caller. So just do not catch the exception and the init "stopps" automatically.

Categories

Resources