Итак, я пытаюсь написать модульный тест для своей деятельности с использованием Robolectric, однако я понятия не имею, как предоставить издевательскую модель представления, видя, как мой виртуальный компьютер создается непосредственно в классе. Это связано с тем, что ViewModel с учетом жизненного цикла реактивного ранца требует создания экземпляра класса Provider. Итак, я, по сути, ввожу пользовательский провайдер, а затем использую его для создания своей ViewModel. Я просмотрел другие примеры, но все они кажутся чрезвычайно запутанными. Как мне этого добиться?
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]
}
}
Вот как я создаю свою 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>)
Я столкнулся с похожей проблемой. Проблема здесь в том, что активность знает, как она внедряется. Попробуйте заглянуть в Dagger Android, это позволит вам внедрять зависимости полей в ваши действия. Однако это потребует хорошего рефакторинга.
@SeanBlahovici В настоящее время я использую внедрение поля в приведенном выше примере. Или вы имели в виду какую-то инъекцию конструктора?
Вы должны внедрить тестовую модель представления из модуля, который appComponent использует для создания здесь зависимостей.
Не создавайте модель представления самостоятельно. Создайте 2 модуля для appComponent, один из которых предоставляет исходные зависимости, а другой — тестовые/фиктивные зависимости. Что-то вроде этого -
@Module
public AppModule {
public ViewModel appViewModel() { // return original here}
}
@Module
public TestAppModule extends AppModule {
public ViewModel appViewModel() { // return test/mock here}
}
В вашем тесте, когда вы создаете свой AppComponent, проходите TestAppModule вместо AppModule, тогда вы получите издевательские зависимости.
Мой компонент инициализируется в классе Application (MvvmDaggerApp). Я не уверен, что ты имеешь в виду. Я не могу создать его в своем тестовом классе.
Для модульных тестов вам нужно будет создать его в коде инициализации теста. Например, в каком-то методе с аннотацией @Setup в случае Mockito.
appComponent все еще вызывается в onCreate моей активности. Я не понимаю, как я смогу предоставить другой компонент для своей деятельности. Не могли бы вы объяснить на примере кода?
Немного опоздал на вечеринку, но я почти сошел с ума, решая ту же проблему. Вы должны создать другое приложение для целей тестирования, которое расширит ваше рабочее приложение. Там вы переопределяете создание компонента, чтобы вернуть TestComponent. И вишенка на торте - в ваших тестах вы добавляете аннотацию ко всему тестовому классу, например @Config(application = TestApplication::class). Надеюсь, это кому-нибудь поможет :)
Можете ли вы показать нам свой класс модульного тестирования?