使用 ActivityViewModels 测试 Android Fragment

ona*_*per 5 testing android unit-testing

我正在尝试对共享相同 ViewModel 的片段运行 android 仪器测试。ViewModel 有一些参数。我使用 koin,但是在测试中使用 ActivityViewModels 时,koin 不会注入 ViewModel。

class SomeFragment() : Fragment() {
    private val viewModel: SomeViewModel by activityViewModels()
    ... more code
}

class SomeFragmentTest() : KoinTest{

    @Before
    fun setup() {
        val viewModel: SomeViewModel = mockk(relaxed = true)

        startKoin { loadKoinModules(listOf(
            module(override = true) { viewModel },
            module(override = true) { factory { appAnalytics } })) }
    }
.... more code
}
Run Code Online (Sandbox Code Playgroud)

收到的消息是

Caused by: java.lang.InstantiationException: java.lang.Class<SomeViewModel> has no zero argument constructor
Run Code Online (Sandbox Code Playgroud)

ona*_*per 8

我找到了解决方案,并发布了这个问题,希望对其他人有帮助。

我将一个视图模型工厂添加到片段中:

class SomeFragment(val factory: (() -> ViewModelProvider.Factory)? = null) : Fragment() {
    private val viewModel: SomeViewModel by activityViewModels(factory)
    ... more code
}

class SomeFragmentTest() : KoinTest{
    private val fragmentFactory : FragmentFactory = mockk() 
    
    @Before
    fun setup() {
        val viewModel: SomeViewModel = mockk(relaxed = true)
        ... some mock related to viewModel
        val viewModelFactory : ViewModelProvider.Factory = mockk()
        every { viewModelFactory.create(SomeViewModel::class.java) } answers { viewModel }
        every { fragmentFactory.instantiate(any(), any()) } answers { SomeFragment{
            viewModelFactory
        }}
    }
    @Test
    fun test_fragment() {
        launchFragmentInContainer<SomeFragment>(
            themeResId = R.style.AppTheme,
            factory = fragmentFactory
        )
        ... some asserts 
    }

.... more code
}
Run Code Online (Sandbox Code Playgroud)

这适用于 prod,因为在 ActivityViewModels 实现中,当工厂为 null 时,将使用默认值:

inline fun <reified VM : ViewModel> Fragment.activityViewModels(
    noinline factoryProducer: (() -> Factory)? = null
) = createViewModelLazy(VM::class, { requireActivity().viewModelStore },
    factoryProducer ?: { requireActivity().defaultViewModelProviderFactory })
Run Code Online (Sandbox Code Playgroud)