I try to mock some methods in the project so that when they are called, a certain value is returned.
But when you run the tests, they fall with the output:
org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Invalid use of argument matchers! 0 matchers expected, 1 recorded:
-> at com.hodzi.stackviewer.questions.detail.QuestionDetailPresenterTest.voteTest(QuestionDetailPresenterTest.kt:69)
This exception may occur if matchers are combined with raw values:
//incorrect:
someMethod(anyObject(), "raw String"); When using matchers, all arguments have to be provided by matchers. For example:
//correct:
someMethod(anyObject(), eq("String by matcher"));
If you run the same code in debug mode and run through all the lines, then when you call shared.getToken (), the value that we specified is returned. But with normal startup, tests fall on this line.
Code:
import com.hodzi.stackviewer.questions.QuestionsInteractor
import com.hodzi.stackviewer.utils.Shared
import com.hodzi.stackviewer.utils.Vote
import org.junit.BeforeClass
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mockito
internal class QuestionDetailPresenterTest {
companion object {
lateinit var presenter: QuestionDetailPresenter
lateinit var view: QuestionDetailView
#BeforeClass #JvmStatic
fun setUp() {
val questionsInteractor: QuestionsInteractor =
Mockito.mock(QuestionsInteractor::class.java)
val shared: Shared =
Mockito.mock(Shared::class.java)
Mockito.`when`(shared.getToken()).thenReturn("23")
// Mockito.doReturn("23").`when`(shared).getToken()
view = Mockito.mock(QuestionDetailView::class.java)
presenter = QuestionDetailPresenter(questionsInteractor, shared)
}
}
#Test
fun voteTest() {
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Mockito.verify(view).goToAuth()
}
}
Shared:
interface Shared {
companion object {
const val KEY_TOKEN: String = "keyToken"
}
fun getToken(): String
fun saveToken(token: String?)
}
Presenter:
class QuestionDetailPresenter(val questionsInteractor: QuestionsInteractor, val shared: Shared) :
BasePresenter<QuestionDetailView>() {
lateinit var question: Question
fun vote(id: Int, vote: Vote) {
print(vote)
if (Strings.isEmptyString(shared.getToken())) {
view?.goToAuth()
return
}
val observable: Observable<out Data> = when (vote) {
Vote.ANSWER_UP -> {
questionsInteractor.answerUpVote(id, shared.getToken())
}
Vote.ANSWER_DOWN -> {
questionsInteractor.answerDownVote(id, shared.getToken())
}
Vote.QUESTION_UP -> {
questionsInteractor.questionUpVote(id, shared.getToken())
}
Vote.QUESTION_DOWN -> {
questionsInteractor.questionDownVote(id, shared.getToken())
}
}
baseObservableData(observable,
{ data ->
run {
Log.d(Const.LOG_TAG, "success")
}
},
{ throwable ->
run {
Log.d(Const.LOG_TAG, "error")
}
}
)
}
}
Thanks!
There's nothing wrong with your mocking of shared, I think the problem is with:
presenter.vote(ArgumentMatchers.anyInt(), Vote.QUESTION_DOWN)
Just use a real Int instead of the ArgumentMatchers.anyInt().
Like
presenter.vote(0, Vote.QUESTION_DOWN)
Matchers are used when matching arguments on a mocked object, for example
val calulator = (mock with Mockito)
when(calculator.divideByTwo(anyInt()).thenReturn(1)
would mean calculator.divideByTwo(int: Int) returns 1 when called with any Int.
When calling methods of real objects to test them (like you do with your presenter), you use real parameters.
Related
There is a fun method0:
private fun method0() {
println("method0 fun")
}
And a var method0 :
var method0 = {
println("method0")
}
It seems they are used the same:
method0()
I found that both occur at the same time, and the fun function has a higher priority when the code calls.
Other than that, is there any difference between them?
The var way of doing it results in a functional object. The lambda content is wrapped as a functional object so it can be passed around like any other instance of a class. It can directly be used as a function parameter, for instance.
var method0 = {
println("method0")
}
fun doSomethingTwice(action: ()->Unit) {
repeat(2) { action() }
}
fun main() {
doSomethingTwice(method0)
}
And since it's marked as a var you can swap it out for a different function:
fun main() {
method0 = { println("hello, world!") }
doSomethingTwice(method0)
}
Note that this way of specifying a function is a little bit heavier since it is wrapping the function in another class instance.
And you can still wrap any "regular" function into a functional object at any time by using :: to avoid doing it until it's necessary.
fun method0() {
println("method0")
}
fun main() {
doSomethingTwice(::method0)
}
I have a huge project and I need to refactor code to LiveData (not Flow). I have an Order and states in ViewModel. I cannot receive this Order in Activity when I observe it. How can I do this? This is my View Model:
private var _basicModel: MutableLiveData<OrderUiState> = MutableLiveData()
val basicModel: LiveData<OrderUiState> get() = _basicModel
sealed class OrderUiState {
object Loading : OrderUiState()
data class OrderFail(val message: String) : OrderUiState()
data class OrderSuccess(val order: Order) : OrderUiState()
}
fun getOrder(orderId: String) {
viewModelScope.launch {
_basicModel.value = OrderUiState.Loading
getOrderUseCase.execute(orderId, { order ->
_basicModel.value = OrderUiState.OrderSuccess(order)
}
}
And now I cannot to get to Order, when I have Succes Sate. My code want from me in Activity order, but I thought, that whan it is success, there it will be, but isn't?
viewModel.basicModel.observe(this) { order ->
when(order){
OrderViewModel.OrderUiState.OrderSuccess(here he want from me order... )
}
}
Can I get to order from this code?
You can do it this way:
viewModel.basicModel.observe(this) { uiState ->
when(uiState) {
is OrderViewModel.OrderUiState.OrderSuccess -> {
val order = uiState.order
// Use the order here
}
}
}
I am trying to write a UnitTest for the kotlin-version of networkBoundResource that can be found on serveral sources with several features
Here is my version of it with marker-comments for the following question.
inline fun <ResultType, RequestType> networkBoundResource(
...
coroutineDispatcher: CoroutineDispatcher
) = flow {
emit(Resource.loading(null)) // emit works!
val data = queryDatabase().firstOrNull()
val flow = if (shouldFetch(data)) {
emit(Resource.loading(data)) // emit works!
try {
saveFetchResult(fetch())
query().map { Resource.success(it) }
} catch (throwable: Throwable) {
onFetchFailed(throwable)
query().map { Resource.error(throwable.toString(), it) }
}
} else {
query().map { Resource.success(it) }
}
emitAll(flow) // emitAll does not work!
}.catch { exception ->
emit(Resource.error("An error occurred while fetching data! $exception", null))
}.flowOn(coroutineDispatcher)
This is one of my UnitTests for this code. The code is edited a bit to focus on my question:
#get:Rule
val testCoroutineRule = TestCoroutineRule()
private val coroutineDispatcher = TestCoroutineDispatcher()
#Test
fun networkBoundResource_noCachedData_shouldMakeNetworkCallAndStoreUserInDatabase() = testCoroutineRule.runBlockingTest {
...
// When getAuthToken is called
val result = networkBoundResource(..., coroutineDispatcher).toList()
result.forEach {
println(it)
}
}
The problem is that println(it) is only printing the Resource.loading(null) emissions. But if you have a look at the last line of the flow {} block, you will see that there should be another emission of the val flow. But this emission never arrives in my UnitTest. Why?
I'm not too sure of the complete behaviour, but essentially you want to get a resource, and current flow is all lumped into the FlowCollector<T> which makes it harder to reason and test.
I have never used or seen the Google code before and if I'm honest only glanced at it. My main take away was it had poor encapsulation and seems to break separations of concern - it manages the resource state, and handles all io work one one class. I'd prefer to have 2 different classes to separate that logic and allows for easier testing.
As simple pseudo code I would do something like this :
class ResourceRepository {
suspend fun get(r : Request) : Resource {
// abstract implementation details network request and io
// - this function should only fulfill the request
// can now be mocked for testing
delay(3_000)
return Resource.success(Any())
}
}
data class Request(val a : String)
sealed class Resource {
companion object {
val loading : Resource get() = Loading
fun success(a : Any) : Resource = Success(a)
fun error(t: Throwable) : Resource = Error(t)
}
object Loading : Resource()
data class Success(val a : Any) : Resource()
data class Error(val t : Throwable) : Resource()
}
fun resourceFromRequest(r : Request) : Flow<Resource> =
flow { emit(resourceRepository.get(r)) }
.onStart { emit(Resource.loading) }
.catch { emit(Resource.error(it)) }
This allows you to massively simplify the actual testing of the resourceFromRequest() function as you only have to mock the repository and one method. This allows you to abstract and deal with the networking and io work elsewhere, independently which again can be tested in isolation.
As #MarkKeen suggested, I now created my own implementation and it works quite well. Compared to the code that is going around on SO, this version now injects the coroutineDispatcher for easier testing, it lets flow take care of error handling, it does not contain nested flows and is imho easier to read and understand, too. There is still the side-effect of storing updated data to the database, but I am too tired now to tackle this.
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.*
inline fun <ResultType, RequestType> networkBoundResource(
crossinline query: () -> Flow<ResultType?>,
crossinline fetch: suspend () -> RequestType,
crossinline saveFetchResult: suspend (RequestType) -> Unit,
crossinline shouldFetch: (ResultType?) -> Boolean = { true },
coroutineDispatcher: CoroutineDispatcher
) = flow<Resource<ResultType>> {
// check for data in database
val data = query().firstOrNull()
if (data != null) {
// data is not null -> update loading status
emit(Resource.loading(data))
}
if (shouldFetch(data)) {
// Need to fetch data -> call backend
val fetchResult = fetch()
// got data from backend, store it in database
saveFetchResult(fetchResult)
}
// load updated data from database (must not return null anymore)
val updatedData = query().first()
// emit updated data
emit(Resource.success(updatedData))
}.onStart {
emit(Resource.loading(null))
}.catch { exception ->
emit(Resource.error("An error occurred while fetching data! $exception", null))
}.flowOn(coroutineDispatcher)
One possible UnitTest for this inline fun, which is used in an AuthRepsitory:
#ExperimentalCoroutinesApi
class AuthRepositoryTest {
companion object {
const val FAKE_ID_TOKEN = "FAkE_ID_TOKEN"
}
#get:Rule
val testCoroutineRule = TestCoroutineRule()
private val coroutineDispatcher = TestCoroutineDispatcher()
private val userDaoFake = spyk<UserDaoFake>()
private val mockApiService = mockk<MyApi>()
private val sut = AuthRepository(
userDaoFake, mockApiService, coroutineDispatcher
)
#Before
fun beforeEachTest() {
userDaoFake.clear()
}
#Test
fun getAuthToken_noCachedData_shouldMakeNetworkCallAndStoreUserInDatabase() = testCoroutineRule.runBlockingTest {
// Given an empty database
coEvery { mockApiService.getUser(any()) } returns NetworkResponse.Success(UserFakes.getNetworkUser(), null, HttpURLConnection.HTTP_OK)
// When getAuthToken is called
val result = sut.getAuthToken(FAKE_ID_TOKEN).toList()
coVerifyOrder {
// Then first try to fetch data from the DB
userDaoFake.get()
// Then fetch the User from the API
mockApiService.getUser(FAKE_ID_TOKEN)
// Then insert the user into the DB
userDaoFake.insert(any())
// Finally return the inserted user from the DB
userDaoFake.get()
}
assertThat(result).containsExactly(
Resource.loading(null),
Resource.success(UserFakes.getAppUser())
).inOrder()
}
}
I am working on an android Application and I opted to use Kotlin Result class so as to handle success/failure on my operations. I made the changes to the code, but the tests stop working and I cannot understand why. Here I show you some snippets:
FireStoreClient.kt
suspend fun items(): Result<ItemsResponse>
NetworkDataSource.kt
suspend fun getItems(): List<Item> =
fireStoreClient.items().fold({ it.items.map { item -> item.toDomain() } }, { emptyList() })
NetworkDataSourceTest.kt
#ExperimentalCoroutinesApi
#Test
fun `Check getItems works properly`() = runBlockingTest {
whenever(fireStoreClient.items()).doReturn(success(MOCK_ITEMS_DOCUMENT))
val expectedResult = listOf(
Item(
id = 1,
desc = "Description 1"
),
Item(
id = 2,
desc = "Description 2"
)
)
assertEquals(expectedResult, dataSource.getItems())
}
And this is the exception I am getting right now. Any clue? It appears that the fold() method is not being executed when unit testing.
java.lang.ClassCastException: kotlin.Result cannot be cast to ItemsResponse
at NetworkDataSource.getItems(NetworkDataSource.kt:31)
I've found a different workaround for this result-wrapping issue, for those who don't want to make their own Result type.
This issue appears to happens specifically when using Mockito's .thenReturn on suspend functions. I've found that using .thenAnswer doesn't exhibit the problem.
So instead of writing this in your unit test (changed doReturn to thenReturn here):
whenever(fireStoreClient.items()).thenReturn(success(MOCK_ITEMS_DOCUMENT))
Use:
whenever(fireStoreClient.items()).thenAnswer { success(MOCK_ITEMS_DOCUMENT) }
Edit: I should note that I was still experiencing this issue when running Kotlin 1.5.0.
Edit: On Kotlin 1.5.20 I can use .thenReturn again.
After a deep dive into the problem, finally, I've found a temporary workaround that works in the testing environment. The problem is, somehow the value of the Result object is wrapped by another Result, and we can pull the desired value or exception using reflection.
So, I've created an extension function called mockSafeFold, which implements the fold behavior in normal calls, and acts fine when you are executing unit-tests.
inline fun <R, reified T> Result<T>.mockSafeFold(
onSuccess: (value: T) -> R,
onFailure: (exception: Throwable) -> R
): R = when {
isSuccess -> {
val value = getOrNull()
try {
onSuccess(value as T)
} catch (e: ClassCastException) {
// This block of code is only executed in testing environment, when we are mocking a
// function that returns a `Result` object.
val valueNotNull = value!!
if ((value as Result<*>).isSuccess) {
valueNotNull::class.java.getDeclaredField("value").let {
it.isAccessible = true
it.get(value) as T
}.let(onSuccess)
} else {
valueNotNull::class.java.getDeclaredField("value").let {
it.isAccessible = true
it.get(value)
}.let { failure ->
failure!!::class.java.getDeclaredField("exception").let {
it.isAccessible = true
it.get(failure) as Exception
}
}.let(onFailure)
}
}
}
else -> onFailure(exceptionOrNull() ?: Exception())
}
Then, simply call it instead of fold:
val result: Result = myUseCase(param)
result.mockSafeFold(
onSuccess = { /* do whatever */ },
onFailure = { /* do whatever */ }
)
I had the same issue.
I noticed that my method of injected class which should return Result<List<Any>> returns actually Result<Result<List<Any>>> which causes the ClassCastException. I used the Evaluate Expression option for the result from the method and I got
Success(Success([]))
The app works well but unit tests didn't pass due this problem.
As a temporary solution I built a new simple implementation of Result sealed class with fold() extension function. It should be easy to replace in future to kotlin.Result
Result sealed class:
sealed class Result<T> {
data class Success<T>(val value: T) : Result<T>()
data class Failure<T>(val error: Throwable) : Result<T>()
}
fold() extension function:
inline fun <R, T> Result<T>.fold(
onSuccess: (value: T) -> R,
onFailure: (exception: Throwable) -> R
): R = when (this) {
is Result.Success -> onSuccess(value)
is Result.Failure -> onFailure(error)
}
I've pretty excited by Kotlin compiler features and by by in particular - it saves time generating gelegating code:
https://kotlinlang.org/docs/reference/delegation.html
But i want delegate to be nullable and delegating code to check if it's null first and return if it is:
interface Base {
val message: String
fun print()
}
class BaseImpl(val x: Int?) : Base {
override val message = "BaseImpl: x = $x"
override fun print() { println(message) }
}
class Derived(b: Base?) : Base by b {
// This property is not accessed from b's implementation of `print`
override val message = "Message of Derived"
}
fun main() {
val b = BaseImpl(10)
val derived = Derived(b)
derived.print()
println(derived.message)
}
When compiling ^ i'm getting Type mismatch: inferred type is Base? but Base was expected.
Is it still possible with Kotlin?
To be more detailed i'd like Kotlin compiler to generate forwarding calls to wrapped impl (extWebChromeClient) in https://developer.android.com/reference/android/webkit/WebChromeClient like follows:
private WebChromeClient intWebChromeClient = new WebChromeClient()
{
#Override
public void onReceivedTitle(WebView view, String title)
{
if (extWebChromeClient != null)
{
extWebChromeClient.onReceivedTitle(view, title);
}
}
...
You can make this yourself using dynamic proxies, though I wouldn't really recommend it. Note that for non-void methods there's no way to require overriding them. The below implementation just throws exceptions for them unconditionally, but you could still call them for non-null x.
inline fun <reified T : Any> nullableProxy(x: T?): T {
val handler = InvocationHandler { _, method, args ->
if (method.returnType == Void.TYPE) {
if (x != null) {
method.invoke(x, *(args ?: arrayOf()))
}
} else
throw UnsupportedOperationException("Non-void method")
}
return Proxy.newProxyInstance(
T::class.java.classLoader,
arrayOf(T::class.java),
handler) as T
}
class Derived(b: Base?) : Base by nullableProxy(b)
This also won't perform as well as implementing methods directly would.