是否可以模拟片段内部的 ViewModel

Is there away to mock ViewModel that is inside of a fragment

有没有办法模拟在片段内部构建的 ViewModel?我正在尝试 运行 对片段进行一些测试,其中一个片段函数与 ViewModel 交互,我想 运行 测试函数并为 ViewModel 提供模拟结果。这甚至可能吗?

我的片段

class MyFragment : Fragment() {
    @Inject
    lateinit var viewModel: MyViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        (requireActivity().application as MyApplication).appComponent.inject(this)
        super.onCreate(savedInstanceState)
    }
    
} 

测试

@RunWith(RoboeltricTestRunner::) {
    
    @Before
    fun setup() {
        FragmentScenario.Companion.launchIncontainer(MyFragment::class.java)
    }
}

是的,只需标记您的 ViewModel open,然后您就可以在其上创建一个模拟实现。

open class MyViewModel: ViewModel() {

    fun myMethodINeedToMock() {
      
    }
}
class MockMyViewModel: MyViewModel() {

    override fun myMethodINeedToMock() {
      // don't call super.myMethodINeedToMock()
    }
}

因此,在测试时将您的 MockMyViewModel 注册到 DI 框架。

我想我会 post 为其他正在努力寻找解决方案的人提供此信息。您需要使用依赖于 ViewModel 的片段工厂。将 ViewModel 注入片段构造函数允许 ViewModel 轻松被模拟。需要为 FragmentFactory 完成几个步骤,但只要完成几个步骤就不会那么复杂。

  1. Fragment 将 ViewModel 添加到构造函数中。
class MyFragment(private val viewModel: ViewModel) : Fragment {

    ... 
}
  1. FragmentFactory,允许片段在构造函数中具有依赖性。
class MyFragmentFactory(private val viewModel: MyViewModel) : FragmentFactory() {
    override fun instantiate(classLoader: ClassLoader, className: String): Fragment {
        return when(className) {

            MyFirstFragment::class.java.name -> {
                MyFragment(viewModel)
            }

            // You could use this factory for multiple Fragments.
            MySecondFragment::class.java.name -> {
                MySecondFragment(viewModel)
            }

            // You also don't have to pass the dependency
            MyThirdFragment::class.java.name -> {
                MyThirdFragment()
            }
            else -> super.instantiate(classLoader, className)
        }
    }
}
  1. 主要Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        // Create your ViewModel
        val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        // create the FragmentFactory and the viewModel dependency.
        supportFragmentManager.fragmentFactory = MainFragmentFactory(viewModel)

        // FragmentFactory needs to be created before super in an activity. 
        super.onCreate(savedInstanceState)

    }
}
  1. 测试
@RunWith(RobolectricTestRunner::class)
class MyFragmentUnitTest {
    
    @Before 
    fun setup() { 
        val viewModel: MainViewModel = mock(MyViewModel::class.java)
        ...
    }
}