Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mock jetpack ViewModel for unit tests using Dagger2 (Robolectric)

So I'm trying to write a unit test for my Activity using Robolectric, however I have no idea how to provide a mocked viewmodel seeing as how my vm is instantiated directly in the class. This is due to the fact that jetpack's lifecycle aware ViewModel requires a Provider class to instantiate. So, I'm essentially injecting the custom provider and then using that to create my ViewModel. I've looked at other examples but they all seem extremely confusing. How do I achieve this?

class ActivityEpisodeList : AppCompatActivity() {

        @Inject
        lateinit var vmFactory: ViewModelProvider.Factory

        private lateinit var vm: ActivityViewModel


        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_episode_list)

            MvvmDaggerApp.get(this).appComponent.inject(this)

            vm = ViewModelProviders.of(this, vmFactory)[ActivityViewModel::class.java]
    }
}

This is how I'm creating my ViewModel:

@Module
abstract class ViewModelModule {

    @Binds
    internal abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(ActivityViewModel::class)
    internal abstract fun postListViewModel(viewModel: ActivityViewModel): ViewModel

}


@Singleton
class ViewModelFactory @Inject constructor(private val viewModels: MutableMap<Class<out ViewModel>, Provider<ViewModel>>) : ViewModelProvider.Factory {

    override fun <T : ViewModel> create(modelClass: Class<T>): T = viewModels[modelClass]?.get() as T
}

@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
@MapKey
internal annotation class ViewModelKey(val value: KClass<out ViewModel>)
like image 495
SpecialSnowflake Avatar asked Feb 19 '19 10:02

SpecialSnowflake


1 Answers

You should inject a Test View Model from the module which appComponent is using to create dependencies here.

Do not create the viewmodel yourself. Create 2 modules for appComponent one which provides the original dependencies and other with test/mock dependencies. Something like this -

@Module
public AppModule {
      public ViewModel appViewModel() { // return original here}
}

  @Module
public TestAppModule extends AppModule {
      public ViewModel appViewModel() { // return test/mock here}
}

In your test when you create Your AppComponent pass TestAppModule instead of AppModule, then you will get mocked dependencies.

like image 127
Vishal Arora Avatar answered Oct 18 '22 00:10

Vishal Arora