当前位置: 首页 > 知识库问答 >
问题:

Espresso生成FileNotFoundException,与Dagger一起使用时

徐峰
2023-03-14

我一直在纠结于一个遗留的Android应用程序,试图为其添加测试和适当的架构。该应用程序有一个主LaunchActivity,它在启动时运行一系列检查。最初,activity使用Dagger很差劲地“注入依赖项”,activity将使用这些依赖项来运行检查。

我转向了MVVM,这样我就可以单独测试视图模型,而不需要检测,并且只需要为UI测试注入一个模拟视图模型。我跟随本文介绍了这些更改,包括切换到使用新的Dagger Android方法,如AndroidInjection.Inject

我想让测试尽可能地指导任何更改,所以当我有了基本的架构工作时,我转而编写UI测试。现在,必须用Dagger将模拟视图模型注入activity是一项相当艰巨的任务,但我认为我已经找到了一个可行的解决方案。

我已经使用了一个带有自定义检测运行器的TestApp来使用DexOpener,我将其修改为还实现了HasActivityInjector,这与我的应用程序的实际自定义App非常相似(两者都扩展了application)。

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

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测试时,尽管测试表明它们已经通过,但在(通过的)视图断言应该在的地方有一个奇怪的堆栈跟踪。

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中错误跟踪到的语句是:

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

我不明白为什么测试显示这个错误。我知道它与Dagger有关,因为如果我注释掉构建DaggerTestAppComponent,就没有问题。但是,如果不使用这个测试组件,我不确定如何将模拟视图模型注入到activity中。某些东西导致Dagger和Espresso不能很好地运行,我认为,某些东西与堆栈跟踪中的daggerbaseLayerComponent有关。但我没有别的了。

我现在唯一的“解决办法”是改用一个片段而不是一个activity,在那里我可以跳过测试中对匕首的需要,并遵循这个示例,但我真的很难理解为什么我会遇到这个问题。如果你能帮我找出原因,我将不胜感激。

共有1个答案

轩辕经国
2023-03-14

这既不是基于非库存ROM,也不是基于Espresso或Dagger,但这是一个已知的问题,
似乎源于AndroidX.test.runner.AndroidJUnitRunnerActivityScenarioFragmentScenario(如上面发布的堆栈跟踪所示)的组合。

当前,不使用activitytestrule可能是解决此问题的唯一选择。只需在问题跟踪器上启动该问题,并在与该问题有关的事情向前移动时得到通知。

 类似资料:
  • 我在使用gradlew(4.10.2)+dagger(2.18)时遇到了一个奇怪的问题。 问题是当我打电话的时候: /commonmodule_providesgsonFactory.java:6:错误:包javax.annotation.processing不存在导入javax.annotation.processing.generated; /commonmodule_providesgson

  • 问题内容: 我正在通过GoDaddy设置SSL,以与AWS EC2上的node.js服务器一起使用。我一直无法启动它。 这是我尝试过的: 打算用于域:files.mysite.com 在服务器上,我运行: 然后,我得到CSR:vim files.mysite.csr 我从以下位置复制并粘贴: 最后还有一个空行,我使用rekey离开并粘贴到GoDaddy界面中。 然后,我下载godaddy密钥,该密

  • 我有一个webpack配置,当我直接调用webpack时,它会生成react包。由于我想合并热重新加载,所以我需要在端口3000上运行的development express服务器(服务APIendpoint)旁边运行webpack dev服务器 webpack.dev.config.js dev-server.js package.json脚本 如果我跑了 结果是一个新的js包和html:dis

  • 基本上,这个IThttps://github.com/ribot/android-boilerplate变成了Kotlin。

  • :此接口包含此组件所需的所有方法。 现在,问题是,如果我在中执行此调用,则此类不是由Dagger生成的。如果调用相同的行,则由dagger生成de类,不会有任何问题。 我寻找的另一个解决方案是创建一个具有相同结构的其他不同类,并将作为内部对象,结果也是一样。