I'm trying to inject mocked ViewModel into Activity for espresso test but activity is getting regular object.
My ActivityTest is able to create required mock but i'm not sure how to access that mock in Activity.
complete app source
val MovieListModule: Module = module {
single {
DummyMovieListRepository() as MovieListRepository
}
viewModel {
MovieListViewModel(respository = get())
}
}
@RunWith(AndroidJUnit4::class)
class HomeScreenActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(HomeScreenActivity::class.java, true, true)
val viewModel: MovieListViewModel by inject()
@Before
fun setup() {
declareMock<MovieListViewModel>(isFactory = true, binds = listOf(ViewModel::class))
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewVisible() {
`when`(viewModel.sayHello())
.thenReturn("hello view-model")
onView(withId(R.id.tv_homescreen_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_homescreen_message))
.check(matches(withText("hello view-model")))
}
}
Yes, it should works. This is the exact way of doing it 馃憤 !
But this doesn't work
Doesn't work for me too. For me works just: declareMock<MovieListViewModel>m but when I try to do e.g. Mockito.verify(viewModel).loadUsers() (assuming that viewModel has this method, I get exception:
org.koin.error.DependencyResolutionException: Multiple definitions found for type...., When I do not touch viewModel, test passes
@arnaudgiuliani are you sure this works in instrumented tests? Isn't there a limitation of not being able to mock final classes?
I tried this approach and I get an exception which looks like the limitation of not being able to
mock final classes on Android JVM:
org.koin.error.BeanInstanceCreationException: Can't create definition for 'Single [name='AccountsListService',class='com.exampe.service.AccountsListService', binds~(com.example.service.AccountsListServiceInterface)]' due to error :
Could not initialize plugin: interface org.mockito.plugins.MockMaker (alternate: null)
Although to be fair I don't see a final class since I'm mocking an interface.
Any ideas?
I'm able to provide mocked viewModel this way
@RunWith(AndroidJUnit4::class)
class HomeScreenActivityTest : KoinTest {
@Rule
@JvmField
val rule = ActivityTestRule(HomeScreenActivity::class.java, true, false)
lateinit var mockVm: MovieListViewModel
@Before
fun setup() {
mockVm = mock(MovieListViewModel::class.java)
loadKoinModules(module {
viewModel {
mockVm
}
})
}
@After
fun cleanUp() {
stopKoin()
}
@Test
fun shouldHaveTextViewWithMessage() {
// 1. declare mock method
val message = "hello view-model"
Mockito.`when`(mockVm.sayHello())
.thenReturn(message)
// 2. start activity
rule.launchActivity(null)
// 3. test
onView(withId(R.id.tv_homescreen_message))
.check(matches(isDisplayed()))
onView(withId(R.id.tv_homescreen_message))
.check(matches(withText(message)))
}
}
on the side note, I started koin with empty module so we can declare only needed dependencies from Test class
class TestApp : Application() {
override fun onCreate() {
super.onCreate()
startKoin(this, emptyList())
}
}
class TestAppJUnitRunner : AndroidJUnitRunner() {
override fun newApplication(cl: ClassLoader?, className: String?, context: Context?): Application {
return super.newApplication(cl, TestApp::class.java.name, context)
}
}
// app module build.gradle
android {
defaultConfig {
testInstrumentationRunner "com.package.TestAppJUnitRunner"
}
}
Most helpful comment
I'm able to provide mocked viewModel this way
on the side note, I started koin with empty module so we can declare only needed dependencies from Test class