如何使视图模型实现测试服务,而Dagger 2设置扩展了DaggerApplication,并使用了Component.Builder

时间:2019-02-15 22:38:30

标签: android kotlin dagger-2 robolectric

在测试期间,我的视图模型仍在使用生产服务,我希望他们使用测试服务。我正在使用robolectric进行测试,但仍然没有找到解决我问题的方法。

我认为最有希望的是在测试片段中使用viewModelFactory生成器,例如GitHubBrowserSample中使用了ViewModelUtil.createFor(mockedViewModel)。但这对我不起作用,如下所示,在运行startFragment时,在模拟的viewModel中创建的测试服务将被生产服务替换。

我的主应用程序扩展了DaggerApplication(),并且我的生产组件使用@ Component.Builder,所以我无法在构建过程中指定模块。

此外,我尝试将TestAppComponent从我的AppComponent扩展出来,但这似乎也不起作用。

我还尝试过将构建器设置为MyApplication.kt中的静态对象,如下所示:

companion object {
    var builder: AndroidInjector.Factory<MyApplication> = 
        DaggerAppComponent.builder()
}

...

override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
    return builder.create(this)
}

然后在我的FragmentTest.kt init中执行以下操作:

    private fun init() {
        bagFragment = BagFragment.newInstance()
        MyApplication.builder = DaggerTestAppComponent.builder()

        SupportFragmentTestUtil.startFragment(bagFragment, AppCompatActivity::class.java)
    }

但是由于类型不匹配而无法正常工作。如果有某种解决方案仍然有可能从DaggerApplication()进行扩展,我对此会非常感兴趣。

这是我的设置:

AppComponent.kt

@Singleton
@Component(modules = [
    AndroidInjectionModule::class,
    AndroidSupportInjectionModule::class,
    AppModule::class,
    ActivityModule::class,
    FragmentModule::class,
    ProductionModule::class])
interface AppComponent : AndroidInjector<MyApplication>{

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<MyApplication>()
}

ProductionModule.kt

@Module
open class ProductionModule {

    @Provides
    fun browseCategoriesViewModel(authenticationService: AuthenticationService): BrowseCategoriesFragment.BrowseCategoriesViewModel{
        return BrowseCategoriesFragment.BrowseCategoriesViewModel(authenticationService)
    }
// Provides multiple things
...

ViewModelModule.kt

@Module
abstract class ViewModelModule {
    @Binds
    abstract fun bindViewModelFactory(factor: ViewModelFactory): ViewModelProvider.Factory

    /*** Fragments ***/
    @Binds
    @IntoMap
    @ViewModelKey(BagFragment.BagViewModel::class)
    abstract fun bindBagViewModel(viewModel: BagFragment.BagViewModel): ViewModel

MyApplication.kt

class MyApplication: DaggerApplication(), HasActivityInjector, HasSupportFragmentInjector {

    @Inject
    lateinit var sharedPreferencesService: SharedPreferencesService

    @Inject
    lateinit var activityInjector: DispatchingAndroidInjector<Activity>

    @Inject
    lateinit var supportFragmentInjector: DispatchingAndroidInjector<Fragment>

    override fun attachBaseContext(base: Context?) {
        super.attachBaseContext(base)
        MultiDex.install(this)
    }

    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().create(this)
    }

    override fun activityInjector() = activityInjector

    override fun supportFragmentInjector() = supportFragmentInjector
}

AppModule,FragmentModule和ActivityModule是根据文档设置的,没有给我任何问题。

这是我的测试设置:

TestAppComponent.kt

@Singleton
@Component(modules =[
    AndroidInjectionModule::class,
    AndroidSupportInjectionModule::class,
    TestModule::class])
interface TestAppComponent: AndroidInjector<MyApplication>{

    @Component.Builder
    abstract class Builder: AndroidInjector.Builder<MyApplication>()
}

TestModule.kt

@Module
internal class TestModule {

    @Provides
    fun bagViewModel(dataService: FakeDataService,
                     authenticationService: FakeAuthenticationService,
                     favoritesService: FakeFavoritesService,
                     cartService: FakeCartService): BagFragment.BagViewModel {
        return BagFragment.BagViewModel(dataService, authenticationService, favoritesService, cartService)
    }

    @Provides
    @Singleton
    fun dataService(): FakeDataService = FakeDataService()

    @Provides
    @Singleton
    fun authenticationService(): FakeAuthenticationService = FakeAuthenticationService()

// provides many other things

我当前的测试片段初始化方法如下:

    private fun init() {
        bagFragment = BagFragment.newInstance()        
        SupportFragmentTestUtil.startFragment(bagFragment, AppCompatActivity::class.java)
    }

最后但并非最不重要的一个示例生产片段:

BagFragment.kt

    @Inject
    lateinit var viewModelFactory: ViewModelProvider.Factory

    lateinit var viewModel: BagViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        AndroidSupportInjection.inject(this)
        super.onCreate(savedInstanceState)
        viewModel = ViewModelProviders.of(this, viewModelFactory).get(BagViewModel::class.java)
    }

当我打电话给SupportFragmentTestUtil.startFragment()时,我假设AndroidInjection.inject(this)将用生产模块替换我的假测试模块。我还尝试过将AndroidInjection.inject(this)放在init方法中,而不是onCreate中,但这给我带来了其他问题,并且与文档相反。请帮忙!

0 个答案:

没有答案