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

问题描述

我在搜索如何将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(),并且可以在没有构造函数参数的情况下注入viewmodel,但无法成功实现。

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中模拟依赖项,但是您必须分别为每个依赖项创建测试,并且不能真正测试整个屏幕的逻辑

解决方法

@HiltViewModel 注释会生成您本来可以编写的绑定模块。

其中之一是名为 BindsModule 的模块。 此类在包含该多绑定模块和键的一个包装器类中声明。

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

的 ViewModel
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()
    }
}
,

实际上,您根本不需要注入viewModel。这应该工作

class ViewModelTest {
    // Your specific ViewModel
    private lateinit var yourViewModel: ViewModel

    // for your repositories that use async task such as coroutines etc.
    @get:Rule
    var instantTaskExecutorRule = InstantTaskExecutorRule()

    @Before
    fun setUp() {
        yourViewModel = ViewModel( // Dependencies you need and stuff )
    }

    @Test
    fun `testing_stuff`() {
        val value = yourViewModel.testingFunction("Bla bla")

        assertThat(value).isEqualTo("Bla bla")
    }
}

dependencies {
    androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
}

这是完整的教程:https://www.youtube.com/watch?v=B-dJTFeOAqw

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...