与Dagger一起使用时,Espresso生成FileNotFoundException [英] Espresso generating FileNotFoundException, when used with Dagger

查看:91
本文介绍了与Dagger一起使用时,Espresso生成FileNotFoundException的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在努力处理旧版Android应用程序,试图为其添加测试和适当的体系结构。该应用程序具有主要的 LaunchActivity ,在启动时会进行一系列检查。最初,该活动使用Dagger来注入依赖项,但活动性较差。

I have been wrestling with a legacy Android app, attempting to add testing and proper architecture to it. The app has a main LaunchActivity which runs a series of checks on launch. Originally, the activity was using Dagger to, rather poorly, 'inject dependencies' that the activity would use to run checks.

我将齿轮转移到了MVVM,这样我就可以无需测试即可分别测试视图模型,并且仅需为UI测试注入模拟的视图模型。我按照这篇文章进行了介绍更改,包括切换到使用新的Dagger Android方法,例如 AndroidInjection.inject

I shifted gears to MVVM, so that I could test the view model separately, without instrumentation, and would only need to inject a mocked view model for UI tests. I followed this article to introduce the changes, including switching to using the new Dagger Android methods like AndroidInjection.inject.

我希望测试可以指导尽我所能进行任何更改,所以当我使用基本架构时,我转向编写UI测试。现在,必须使用Dagger将模拟视图模型注入活动中被证明是一项艰巨的任务,但我认为我已经找到了可行的解决方案。

I want the tests to guide any changes as much as I can, so when I had the basic architecture working, I switched to writing UI tests. Now, having to inject a mock view model into the activity with Dagger was proving to be quite a task, but I think I have reached a workable solution.

我已经使用 TestApp 和自定义工具运行器使用 DexOpener ,我将其更改为也实现了 HasActivityInjector ,非常类似于我的应用程序的实际自定义 App (都扩展了 Application )。

I was already using a TestApp with a custom instrumentation runner to use DexOpener, which I changed to also implement HasActivityInjector, much like the actual custom App for my application (both extend Application).

对于Dagger,我创建了单独的模块和用于测试的组件:

For Dagger, I created separate modules and a component for testing:

TestAppComponent >

@Component(
        modules = [
            TestDepsModule::class,
            TestViewModelModule::class,
            TestAndroidContributorModule::class,
            AndroidSupportInjectionModule::class
        ]
)
@Singleton
interface TestAppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        fun testViewModelModule(testViewModelModule: TestViewModelModule): Builder

        fun build(): TestAppComponent
    }

    fun inject(app: TestFieldIApp)
}

TestViewModelModule

@Module
class TestViewModelModule {
    lateinit var mockLaunchViewModel: LaunchViewModel

    @Provides
    fun bindViewModelFactory(factory: TestViewModelFactory): ViewModelProvider.Factory {
        return factory
    }

    @Provides
    @IntoMap
    @ViewModelKey(LaunchViewModel::class)
    fun launchViewModel(): ViewModel {
        if(!(::mockLaunchViewModel.isInitialized)) {
            mockLaunchViewModel = mock(LaunchViewModel::class.java)
        }
        return mockLaunchViewModel
    }
}

TestAndroidConributorModule

@Module
abstract class TestAndroidContributorModule {
    @ContributesAndroidInjector
    abstract fun contributeLaunchActivity(): LaunchActivity
}

然后,在 LaunchActivityTest ,我有:

@RunWith(AndroidJUnit4::class)
class LaunchActivityTest {
    @Rule
    @JvmField
    val activityRule: ActivityTestRule<LaunchActivity> = ActivityTestRule(LaunchActivity::class.java, true, false)

    lateinit var viewModel: LaunchViewModel

    @Before
    fun init() {
        viewModel = mock(LaunchViewModel::class.java)

        val testApp: TestLegacyApp = InstrumentationRegistry.getInstrumentation().targetContext.applicationContext as TestLegacyApp

        val testViewModelModule: TestViewModelModule = TestViewModelModule()
        testViewModelModule.mockLaunchViewModel = viewModel

        DaggerTestAppComponent
                .builder()
                .application(testApp)
                .testViewModelModule(testViewModelModule)
                .build()
                .inject(testApp)
    }

    @Test
    fun whenHideInstructionsIsFalse_showsInstructions() {
        `when`(viewModel.hideInstructions).thenReturn(false)

        activityRule.launchActivity(null)

        onView(withId(R.id.launch_page_slider)).check(matches(isDisplayed()))
        onView(withId(R.id.launch_progress_view)).check(matches(not(isDisplayed())))
    }

    @Test
    fun whenHideInstructionsIsTrue_doesNotShowInstructions() {
        `when`(viewModel.hideInstructions).thenReturn(true)

        activityRule.launchActivity(null)

        onView(withId(R.id.launch_page_slider)).check(matches(not(isDisplayed())))
        onView(withId(R.id.launch_progress_view)).check(matches(isDisplayed()))
    }
}

结果是视图模型被正确地模拟,因此其他所有内容都应工作...但是,当运行Espresso测试时,尽管测试表明它们已经通过,但在(通过的)视图中却有一个奇怪的堆栈跟踪

The result is that the view model is being properly mocked, so everything else should work... But, when the Espresso tests are run, although the tests show that they have passed, there is a strange stack trace where the (passing) view assertions ought to be.

E/System: Unable to open zip file: /data/user/0/com.myapps.android.legacyapp/cache/qZb3CT3H.jar
E/System: java.io.FileNotFoundException: File doesn't exist: /data/user/0/com.myapps.android.legacyapp/cache/qZb3CT3H.jar
        at java.util.zip.ZipFile.<init>(ZipFile.java:215)
        at java.util.zip.ZipFile.<init>(ZipFile.java:152)
        at java.util.jar.JarFile.<init>(JarFile.java:160)
        at java.util.jar.JarFile.<init>(JarFile.java:97)
        at libcore.io.ClassPathURLStreamHandler.<init>(ClassPathURLStreamHandler.java:47)
        at dalvik.system.DexPathList$Element.maybeInit(DexPathList.java:702)
        at dalvik.system.DexPathList$Element.findResource(DexPathList.java:729)
        at dalvik.system.DexPathList.findResources(DexPathList.java:526)
        at dalvik.system.BaseDexClassLoader.findResources(BaseDexClassLoader.java:174)
        at java.lang.ClassLoader.getResources(ClassLoader.java:839)
        at java.util.ServiceLoader$LazyIterator.hasNextService(ServiceLoader.java:349)
        at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:402)
        at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:488)
        at androidx.test.internal.platform.ServiceLoaderWrapper.loadService(ServiceLoaderWrapper.java:46)
        at androidx.test.espresso.base.UiControllerModule.provideUiController(UiControllerModule.java:42)
        at androidx.test.espresso.base.UiControllerModule_ProvideUiControllerFactory.provideUiController(UiControllerModule_ProvideUiControllerFactory.java:36)
        at androidx.test.espresso.base.UiControllerModule_ProvideUiControllerFactory.get(UiControllerModule_ProvideUiControllerFactory.java:26)
        at androidx.test.espresso.base.UiControllerModule_ProvideUiControllerFactory.get(UiControllerModule_ProvideUiControllerFactory.java:9)
        at androidx.test.espresso.core.internal.deps.dagger.internal.DoubleCheck.get(DoubleCheck.java:51)
        at androidx.test.espresso.DaggerBaseLayerComponent$ViewInteractionComponentImpl.viewInteraction(DaggerBaseLayerComponent.java:239)
        at androidx.test.espresso.Espresso.onView(Espresso.java:84)
        at com.myapps.android.legacyapp.tests.ui.launch.LaunchActivityTest.whenHideInstructionsIsFalse_showsInstructions(LaunchActivityTest.kt:64)

中的语句LaunchActivityTest 错误跟踪到的位置是:

The statement in LaunchActivityTest where the error traces to is:

onView(withId(R.id.launch_page_slider)).check(matches(isDisplayed()))

我不知道为什么测试显示这个错误。我知道这与Dagger有关,因为如果我注释掉构建 DaggerTestAppComponent ,就没有问题。但是,在不使用此测试组件的情况下,我不确定如何将模拟视图模型注入活动中。某些原因导致Dagger和Espresso的播放效果不佳,我认为这与堆栈跟踪中的 DaggerBaseLayerComponent 有关。但是我什么也没有。

I can't figure out why the test is showing this error. I know it's something related to Dagger, because if I comment out building DaggerTestAppComponent, there is no issue. But, without using this test component, I'm not sure how I can inject the mocked view model into the activity. Something is causing Dagger and Espresso to not play nicely, something, I think, related to this DaggerBaseLayerComponent in the stack trace. But I have nothing else.

我目前唯一的解决方案是切换到片段而不是活动,在这里我可以完全跳过测试中对Dagger的需求并按照此示例进行操作,但我对此感到很困惑为什么我遇到这个问题。在找出原因方面,我将不胜感激。

The only 'solution' I have presently is switching to a Fragment instead of an Activity, where I could skip the need for Dagger in tests altogether and follow this sample, but I'm really baffled as to why I'm getting this issue. I would greatly appreciate any help in finding out the reason.

推荐答案

这既不是基于非常规ROM也不是Espresso或匕首,但这是已知的问题
似乎源自 androidx.test.runner.AndroidJUnitRunner ActivityScenario FragmentScenario 结合使用(如此处发布的堆栈跟踪所示)。

This is neither based upon a non-stock ROM nor Espresso or Dagger, but it's a known issue,
which appears to stem from the androidx.test.runner.AndroidJUnitRunner in combintion with ActivityScenario or FragmentScenario (as the stack-traces posted there may suggest).

不使用 ActivityTestRule 当前可能是解决此问题的唯一选择。只需在问题跟踪器上为问题加注星标,并在有关该问题的事情进展时得到通知即可。

Not using an ActivityTestRule might currently be the only option to work around this. Just star the issue on the issue-tracker and get notified, when something moves forward concerning the issue.

这篇关于与Dagger一起使用时,Espresso生成FileNotFoundException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆