You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Hilt环境下断开与Firebase模拟器的连接

问题分析

Firebase Auth 和 Firestore 是全局单例对象,测试中调用 useEmulator 修改了单例的配置后,开发环境运行时会复用这个已被修改的单例,导致连接到模拟器而非生产 Firebase 服务。


解决方案

方案1:测试使用独立 FirebaseApp 实例(推荐)

创建测试专用的 FirebaseApp 实例,与开发环境的默认实例完全隔离,避免配置互相影响。

修改测试模块的 FakeFirebaseModule:

@Module
@TestInstallIn(components = [SingletonComponent::class], replaces = [FirebaseModule::class])
object FakeFirebaseModule {
    private val TAG = FakeFirebaseModule::class.simpleName
    private const val TEST_APP_NAME = "TestFirebaseApp"

    @Singleton
    @Provides
    fun provideTestFirebaseApp(context: Context): FirebaseApp {
        // 复用已存在的测试实例,否则创建新实例
        return try {
            FirebaseApp.getInstance(TEST_APP_NAME)
        } catch (e: IllegalStateException) {
            val options = FirebaseOptions.fromResource(context)
            FirebaseApp.initializeApp(context, options, TEST_APP_NAME)
        }
    }

    @Singleton
    @Provides
    fun provideAuth(app: FirebaseApp): FirebaseAuth = FirebaseAuth.getInstance(app).apply {
        try {
            useEmulator("10.0.2.2", 9099)
        } catch (e: IllegalStateException) {
            Log.e(TAG, "User emulator failed", e)
        }
    }

    @Singleton
    @Provides
    fun provideDb(app: FirebaseApp): FirebaseFirestore = FirebaseFirestore.getInstance(app).apply {
        try {
            useEmulator("10.0.2.2", 8080)
        } catch (e: IllegalStateException) {
            Log.e(TAG, "User emulator failed", e)
        }
        firestoreSettings = FirebaseFirestoreSettings.Builder().setPersistenceEnabled(false).build()
    }
}

测试使用独立的 TestFirebaseApp 实例,开发环境继续使用默认 Firebase 实例,两者配置完全独立。

方案2:开发模块强制重置配置

在开发环境的 FirebaseModule 中,强制恢复 Firebase 实例到生产环境配置,覆盖测试留下的模拟器设置。

修改开发模块的 FirebaseModule:

@Module
@InstallIn(SingletonComponent::class)
object FirebaseModule {
    @Singleton
    @Provides
    fun provideAuth(): FirebaseAuth = Firebase.auth.apply {
        // 通过反射重置 Auth 模拟器配置(依赖 Firebase 内部实现,版本更新可能失效)
        try {
            val emulatorSettingsField = FirebaseAuth::class.java.getDeclaredField("emulatorSettings")
            emulatorSettingsField.isAccessible = true
            emulatorSettingsField.set(this, null)
        } catch (e: Exception) {
            // 反射失败时忽略,默认使用生产环境
        }
    }

    @Singleton
    @Provides
    fun provideDb(): FirebaseFirestore = Firebase.firestore.apply {
        // 重新设置 Firestore 配置,清除模拟器设置
        firestoreSettings = FirebaseFirestoreSettings.Builder().build()
        // 开发环境自定义设置可在此添加
    }
}

方案3:测试后清理配置

在测试的 @After 方法中重置 Firebase 实例配置,降低开发环境被影响的概率。

修改测试代码:

@UninstallModules(FirebaseModule::class)
@HiltAndroidTest
@MediumTest
class ExampleTest {
    private val TAG = ExampleTest::class.simpleName
    // ... 现有代码 ...

    @After
    fun tearDown() {
        // 重置 Auth 模拟器配置
        try {
            val emulatorSettingsField = FirebaseAuth::class.java.getDeclaredField("emulatorSettings")
            emulatorSettingsField.isAccessible = true
            emulatorSettingsField.set(auth, null)
        } catch (e: Exception) {
            Log.e(TAG, "Failed to reset Auth emulator config", e)
        }

        // 重置 Firestore 模拟器配置
        db.firestoreSettings = FirebaseFirestoreSettings.Builder().build()
    }
}

注:若测试崩溃或未正常执行 @After,配置仍可能残留,可靠性不如方案1。


内容的提问来源于stack exchange,提问作者Eva

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 18:48:38