icerockdev / moko-mvvm

Model-View-ViewModel architecture components for mobile (android & ios) Kotlin Multiplatform development
https://moko.icerock.dev/
Apache License 2.0
1k stars 95 forks source link

question about unit testing MutableLiveData #44

Closed AliGolmirzaei closed 4 years ago

AliGolmirzaei commented 4 years ago

Hello I am start writing multi platform code using moko-mvvm. Now I want to write unit tests for my common logic which is using MutableLiveData. At first run I encounter the error: java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked which seems to be related to android. Searching a bit i found this as a solution. Problem is it requires InstantTaskExecutorRule which is part of android platform and thus is not accessible in common code. I know I can move my test to android source set and it will goes well but I think its more clean to hold common tests in common source sets. So does anyone have a suggestion how to fix this?

AliGolmirzaei commented 4 years ago

I tried using expect actual keywords and writing a method/class to provide the rule to my common code but none worked. In method one I still get the error and class one produce a compilation error cannot extend final class

Alex009 commented 4 years ago

hello! please check this mergerequest - https://github.com/icerockdev/moko-mvvm/pull/43 specially BaseTestsClass in tests directory

AliGolmirzaei commented 4 years ago

Thank you very much. good luck

AliGolmirzaei commented 4 years ago

Hello again. In the process of unit testing I reached another problem. I want to test my viewmodel which takes an instance of EventsDispatcher. Now the problem is there is no chance for me to make a testable event dispatcher which just run the event in the same thread (like live data) Probably making event dispatcher class open would allow us to create a test event dispatcher

AliGolmirzaei commented 4 years ago

ok I figure out how to do it. I mocked the event dispatcher. For anyone reach here:

val dispatcher = mockk<EventsDispatcher<AuthenticationViewModel.EventsListener>>()
val listener = mockk<AuthenticationViewModel.EventsListener>()
justRun { listener.openMainScreen() }
every { dispatcher.dispatchEvent(any()) } answers {
    firstArg<AuthenticationViewModel.EventsListener.() -> Unit>()(listener)
}
val viewModel = AuthenticationViewModel(dispatcher)
viewModel.login("user", "pass").join()
verify { listener.openMainScreen() }

pseudo code of AuthenticationViewModel:

class AuthenticationViewModel(
    override val eventsDispatcher: EventsDispatcher<EventsListener>
) : BaseViewModel(), EventsDispatcherOwner<AuthenticationViewModel.EventsListener> {
    fun login(user: String, pass: String): Job

    interface EventsListener {
        fun openMainScreen()
        fun onError()
    }
}