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

2024-05-16

我一直在研究旧版 Android 应用程序,尝试为其添加测试和适当的架构。该应用程序有一个主要LaunchActivity它在启动时运行一系列检查。最初,该活动使用 Dagger 来“注入依赖项”,活动将使用它来运行检查,但效果相当糟糕。

我转向 MVVM,这样我就可以单独测试视图模型,无需使用仪器,并且只需要为 UI 测试注入模拟视图模型。我跟着本文 https://android.jlelse.eu/7-steps-to-implement-dagger-2-in-android-dabc16715a3a介绍这些更改,包括切换到使用新的 Dagger Android 方法,例如AndroidInjection.inject.

我希望测试能够尽可能地指导任何更改,因此当我的基本架构正常工作时,我转而编写 UI 测试。现在,事实证明,必须使用 Dagger 将模拟视图模型注入到活动中是一项艰巨的任务,但我认为我已经找到了一个可行的解决方案。

我已经在使用了TestApp与自定义仪器运行器一起使用DexOpener,我将其更改为也实施HasActivityInjector,很像实际的习俗App对于我的应用程序(两者都扩展Application).

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

测试应用组件

@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)
}

测试视图模型模块

@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
    }
}

测试AndroidContributor模块

@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,没有问题。但是,如果不使用此测试组件,我不确定如何将模拟视图模型注入到活动中。有些东西导致 Dagger 和 Espresso 不能很好地发挥作用,我认为与此有关DaggerBaseLayerComponent在堆栈跟踪中。但我没有别的事了。

我目前唯一的“解决方案”是切换到 Fragment 而不是 Activity,这样我就可以在测试中完全跳过 Dagger 的需要并遵循这个样本 https://github.com/googlesamples/android-architecture-components/tree/master/GithubBrowserSample,但我真的很困惑为什么会遇到这个问题。我将非常感谢任何帮助找出原因的帮助。


这既不是基于非库存 ROM,也不是基于 Espresso 或 Dagger,但它是一个已知的issue https://issuetracker.google.com/issues/146054844,
这似乎源于androidx.test.runner.AndroidJUnitRunner结合ActivityScenario or FragmentScenario(正如那里发布的堆栈跟踪可能暗示的那样)。

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

与 Dagger 一起使用时,Espresso 生成 FileNotFoundException 的相关文章

  • 检查双精度值的等于和不等于条件

    我在比较两者时遇到困难double values using and 我创建了 6 个双变量并尝试进行比较If健康 状况 double a b c d e f if a b c d e f My code here in case of t
  • 启动 Twitter 应用程序 [重复]

    这个问题在这里已经有答案了 可能的重复 Twitter 应用程序的 Android Intent https stackoverflow com questions 2077008 android intent for twitter ap
  • 无法解析符号 FlutterActivity

    我使用 VCS gt Checkout from Version Control 将 flutter 项目从 github 导入到 Android Studio 中 现在我面临的问题是 Cannot resolve symbol Flutt
  • 在 Android 2.2 上运行 HelloCordova 时找不到类“android.webkit.WebResourceResponse”

    我尝试按照本教程进行操作 http docs phonegap com en 2 7 0 guide getting started android index md html Getting 20 Started 20with 20 An
  • Android SDK 中可用的所有“android.intent.action”操作的详尽列表是什么?

    大家好 我想知道标准 Android SDK 中定义的所有 Intent 操作是否有详尽的参考 我正在考虑完整的 android intent action someaction 名称 而不是 Intent 类中定义的方便别名操作的限制列表
  • OpenCV InRange 参数

    我在 Android 上使用 OpenCV 来实时查找特定颜色的圆圈 我的第一步是仅保留与我正在寻找的定义颜色相对应的像素 在本例中为红色或绿色 示例图像 https i stack imgur com CIozU jpg 为此 我正在使用
  • 6:需要显示BuyFlow UI

    There is a problem when i am click on payWithGoogle Button I am implementing Google Pay in my Android Application and wh
  • 如何检测 Google Play 上是否有我的应用程序的更新? [复制]

    这个问题在这里已经有答案了 有没有办法以编程方式检查 Google Play 上我的应用程序是否有更新 以便通知用户 我知道 android google play 有自动通知 但我想使用我自己的通知 弹出消息来更新可用性 有点像 Vibe
  • 如何在 Android 清单文件中设置文本外观?

    是否可以做相当于 setTextAppearance context android R style TextAppearance Medium 在 Android 应用程序的清单文件中 android textAppearance and
  • Locale.getDefault().getCountry() 返回空字符串

    我正在尝试使用国家 地区代码获取用户语言 例如en US es es 但是当我使用Locale getDefault getCountry 它返回空字符串 虽然它给了我正确的语言Locale getDefault getLanguage N
  • 更改android中禁用按钮的颜色

    有没有办法通过样式或其他形式更改 android 中禁用按钮的颜色 我目前有以下内容 可绘制 button default xml
  • Android开发:未定义方法

    大家好 我是 Android 和 Eclipse 的新手 我刚刚遵循了developer android com 上的教程 现在我在添加操作栏 http developer android com training basics actio
  • Enzyme - 测试嵌套组件是否正确呈现

    我正在尝试测试当通过简单的布尔值更新状态时 在父组件中其子组件是否正确呈现 在父组件下面 class Parent extends Component render const isReady this state const props
  • 如何在android中录制音频时暂停背景音乐

    我正在 Android 中开发一个音频记录应用程序 因此 如果设备音乐播放器中已播放任何背景音乐 则应在开始录制之前暂停该背景音乐 并且每当录制停止或暂停时 背景音乐都应恢复 播放录制的音频时也应该如此 有人可以帮我解决这个问题吗 提前致谢
  • 无法在 Android 模拟器中安装 apk

    我正在尝试通过 adb shell 在 ICS 模拟器中安装 apk 从一个站点下载 但出现以下错误 失败 INSTALL FAILED UID CHANGED 可能是什么问题 只需 rm r 有问题的数据目录即可 如果您在安装时遇到此错误
  • Android:如何使视图增长以填充可用空间?

    这看起来很简单 但我不知道该怎么做 我有一个带有 EditText 和两个 ImageButtons 的水平布局 我希望 ImageButtons 具有固定大小 并且 EditText 占据布局中的剩余空间 如何才能做到这一点
  • 带有空白白屏的 WebView

    我在 DialogFragment 中有一个 webview 它使用以下方式显示文档和 PDF它可以进行几次尝试 但如果用户尝试频繁打开和关闭对话框 webview 将显示空白屏幕 我已经尝试了所有的线程link1 https stacko
  • Spock模拟inputStream导致无限循环

    我有一个代码 gridFSFile inputStream bytes 当我尝试这样测试时 given def inputStream Mock InputStream def gridFSDBFile Mock GridFSDBFile
  • 如何创建垂直的BottomNavigationView android?

    我有一个活动 顶部有 4 个片段 底部导航视图有 4 个项目 它在移动设备上运行良好 当我使用 LANDASCAPE 平板电脑时 我想将 BottomNavigationView 移动到活动的左侧 垂直方向如下所示 这是使用 BottomN
  • 不调用 PreviewCallback 和带缓冲区的 PreviewCallback

    我对 Android 4 0 x 的预览回调有疑问 我设置了一个相机 创 建一个表面来显示相机图像on previewCallback 事件 一切正常 但对于 Android 4 0 x 则不然onPreviewCallback被称为 也不

随机推荐