将视图模型注入到仪器测试中

Ale*_*lex 1 testing android android-espresso dagger-2 dagger-hilt

我搜索了很多关于如何将 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())
    }
Run Code Online (Sandbox Code Playgroud)

我试图在测试类中实现 byViewModels() 并且可以在没有构造函数参数的情况下注入视图模型,但没有成功。

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

原因:我希望能够完全测试我的屏幕逻辑,因为 viewModel 将处理对交互器等的依赖。在各种数据流动的背后可能有很多逻辑。测试片段很可能是可能的,但在具有大量测试的较大项目中会较慢。

我已经阅读了https://developer.android.com/jetpack/guide#test-components,它建议进行 JUnit 测试并模拟 viewModel 中的依赖项,但是您必须单独为每个依赖项创建测试并且无法真正测试整个屏幕的逻辑

mvb*_*nes 5

@HiltViewModel注释,生成绑定模块,你会写,否则。

其中之一是名为BindsModule的模块。此类在包含该多绑定模块以及密钥的包装器类中声明。

例如,假设您创建了一个名为的 ViewModel MyViewModel

package com.mypackage

@HiltViewModel
class MyViewModel @Inject constructor(
    private val someDependency: MyType
) : ViewModel()
Run Code Online (Sandbox Code Playgroud)

然后生成的模块看起来像这样:

@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";
    }
  }
}

Run Code Online (Sandbox Code Playgroud)

因此,您的 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个答案误解了这个问题。问题是如何将“真实”ViewModel 注入测试中,而不是如何用依赖图上的模拟替换它 (21认同)
  • 这是一个来自 [mockito-kotlin 库](https://github.com/mockito/mockito-kotlin) 的mockito 扩展,而不是 jetpack 的一部分。`mockMyViewModel` 可以是任何模拟类,也可以是由 `MyViewModel` 的子类制成的“假”类,只要类型相同,它就会替换绑定。 (2认同)