Внедрение модели вида рукоятки в контрольно-измерительные испытания

Я довольно много искал, как внедрить ViewModel в тесты, чтобы проверить это. Допустим, у модели представления есть инъекция конструктора с некоторым взаимодействующим элементом бизнес-логики. Я могу легко вводить его во фрагменты, но безуспешно в тестах.

@HiltAndroidTest
class ViewModelTest

 val randomViewmodel: RandomViewmodel// now what ? since by viewModels() is not accessible in tests

    @Test
    fun viewModelTet() {
        randomViewmodel.triggerAction()
        assertEquals(RandomVIewState(1), randomViewmodel.getState())
    }

Я попытался реализовать byViewModels () в тестовом классе и мог ввести модель просмотра без аргументов конструктора, но безуспешно.

class RandomViewmodel @ViewModelInject constructor(
     private val randomInteractor: RandomInteractor
) : ViewModel
Caused by: java.lang.InstantiationException: class app.RandomViewModel has no zero argument constructor

Причина: я хочу иметь возможность полностью протестировать логику моего экрана, поскольку viewModel будет обрабатывать зависимости от взаимодействующих устройств и т. Д., Может быть довольно много логики с различными потоками данных. Скорее всего, можно было бы протестировать фрагмент, но гораздо медленнее в более крупном проекте с большим количеством тестов.

Я уже читал https://developer.android.com/jetpack/guide#test-components, в которых предлагается выполнять тесты JUnit и высмеивать зависимости в viewModel, но тогда вам нужно создавать тесты для каждой зависимости отдельно, и вы не можете действительно протестировать логику для всего экрана.


person Alex    schedule 23.08.2020    source источник


Ответы (1)


Аннотация @HiltViewModel генерирует модули привязки, которые вы в противном случае написали бы.

Один из них - это модуль под названием BindsModule. Этот класс объявлен внутри класса-оболочки, который содержит этот модуль с несколькими привязками, а также модуль для ключа.

Например, предположим, вы создали ViewModel под названием MyViewModel

package com.mypackage

@HiltViewModel
class MyViewModel @Inject constructor(
    private val someDependency: MyType
) : ViewModel()

Тогда сгенерированный модуль будет выглядеть примерно так:

@OriginatingElement(
    topLevelClass = MyViewModel.class
)
public final class MyViewModel_HiltModules {
  private MyViewModel_HiltModules() {
  }

  @Module
  @InstallIn(ViewModelComponent.class)
  public abstract static class BindsModule {
    private BindsModule() {
    }

    @Binds
    @IntoMap
    @StringKey("com.mypackage.MyViewModel")
    @HiltViewModelMap
    public abstract ViewModel binds(MyViewModel vm);
  }

  @Module
  @InstallIn(ActivityRetainedComponent.class)
  public static final class KeyModule {
    private KeyModule() {
    }

    @Provides
    @IntoSet
    @HiltViewModelMap.KeySet
    public static String provide() {
      return "com.mypackage.MyViewModel";
    }
  }
}

Поэтому ваша ViewModel может заменить этот @Binds контракт, просто используя аннотацию @BindValue для свойства в вашем тестовом классе, которое соответствует типу реализации, в данном случае это будет MyViewModel.

Нет необходимости удалять какие-либо модули, связанные с ViewModel.

@HiltAndroidTest
class MyFragmentInstrumentedUnitTest {
    @get:Rule val hiltRule = HiltAndroidRule(this)

    // either a subclass or a mock, as long as the types match
    // it will provide this instance as the implementation of the abstract binding 
    // `public abstract ViewModel binds(MyViewModel vm);`
    @BindValue
    val mockMyViewModel= mock<MyViewModel>()

    @Before
    fun init() {
        hiltRule.inject()
    }
}
person Virco    schedule 30.04.2021