Android单元测试报错:Looper.getMainLooper未Mock问题求助
问题排查:LiveData转Flow测试报错
Method getMainLooper in android.os.Looper not mocked 相关代码
Repository实现类
class RepositoryImpl( private val service: Service, private val dao: Dao, private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main, ): Repository { override suspend fun getDataListFromDbAsync(): Flow<List<Item>> = withContext(mainDispatcher) { dao.getDataListAsync().asFlow() } /**Some other code*/ }
测试类代码
class RepositoryImplTest { private val dao: Dao = mockk(relaxed = true) private val service: Service = mockk(relaxed = true) private val standardCoroutineDispatcher = StandardTestDispatcher() private val testCoroutineScope = TestScope(standardCoroutineDispatcher) private val repository = RepositoryImpl(service, dao, standardCoroutineDispatcher) @get:Rule val rule = InstantTaskExecutorRule() @BeforeEach fun setUp() { Dispatchers.setMain(standardCoroutineDispatcher) } @AfterEach fun tearDown() { unmockkAll() Dispatchers.resetMain() } @Test fun getDataListFromDbAsync() = testCoroutineScope.runTest { val dbList = mutableListOf<Item>() dbList.add(Item().apply { id = 0 name = "test 0" priority = 0 }) dbList.add(Item().apply { id = 1 name = "test 1" priority = 1 }) dbList.add(Item().apply { id = 2 name = "test 2" priority = 2 }) val mutableLiveData = MutableLiveData<List<Item>>(dbList) val liveData: LiveData<List<Item>> = mutableLiveData.value?.let { MutableLiveData(it) }!! every { dao.getDataListAsync() } returns liveData val resultData = repository.getDataListFromDbAsync().toList().first() Assertions.assertEquals(dbList.toList()[0], resultData[0]) } }
报错信息
Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details. java.lang.RuntimeException: Method getMainLooper in android.os.Looper not mocked. See http://g.co/androidstudio/not-mocked for details. at android.os.Looper.getMainLooper(Looper.java) at androidx.arch.core.executor.DefaultTaskExecutor.isMainThread(DefaultTaskExecutor.java:78) at androidx.arch.core.executor.ArchTaskExecutor.isMainThread(ArchTaskExecutor.java:108) at androidx.lifecycle.LiveData.assertMainThread(LiveData.java:486) at androidx.lifecycle.LiveData.observeForever(LiveData.java:224) at androidx.lifecycle.FlowLiveDataConversions$asFlow$1$1.invokeSuspend(FlowLiveData.kt:97) (Coroutine boundary) at androidx.lifecycle.FlowLiveDataConversions$asFlow$1.invokeSuspend(FlowLiveData.kt:96) at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:230) at kotlinx.coroutines.flow.FlowKt__CollectionKt.toCollection(Collection.kt:26) at com.package.name.repository.RepositoryImplTest$getDataListFromDbAsync$1.invokeSuspend(RepositoryImplTest.kt:77) at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$2.invokeSuspend(TestBuilders.kt:208)
问题原因
报错核心是:LiveData的observeForever方法会检查当前线程是否为主线程,JUnit测试环境中Android系统的Looper相关方法未被Mock,导致线程判断逻辑失败。虽然你设置了Coroutine的Main Dispatcher为测试调度器,但ArchTaskExecutor(LiveData内部依赖它判断线程)并没有被替换为测试用执行器,所以依然触发了主线程检查报错。
解决方案
方案1:替换ArchTaskExecutor为测试执行器
在测试类的setUp和tearDown方法中添加逻辑,让LiveData认为当前线程是主线程,无需修改业务代码:
@BeforeEach fun setUp() { Dispatchers.setMain(standardCoroutineDispatcher) // 替换ArchTaskExecutor,让LiveData跳过真实主线程检查 ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() { override fun executeOnDiskIO(runnable: Runnable) = runnable.run() override fun postToMainThread(runnable: Runnable) = runnable.run() override fun isMainThread(): Boolean = true }) } @AfterEach fun tearDown() { unmockkAll() Dispatchers.resetMain() // 恢复原生ArchTaskExecutor ArchTaskExecutor.getInstance().setDelegate(null) }
方案2:直接测试LiveData(避免转Flow)
如果测试目的仅为验证数据正确性,可以使用LiveData的测试扩展,跳过转Flow的步骤:
// 确保依赖androidx.lifecycle:lifecycle-livedata-ktx的测试库 @Test fun getDataListFromDbAsync() = testCoroutineScope.runTest { // 保留原有数据准备逻辑 val dbList = mutableListOf<Item>().apply { add(Item().apply { id=0; name="test 0"; priority=0 }) add(Item().apply { id=1; name="test 1"; priority=1 }) add(Item().apply { id=2; name="test 2"; priority=2 }) } val liveData = MutableLiveData<List<Item>>(dbList) every { dao.getDataListAsync() } returns liveData // 直接测试LiveData liveData.test { val data = awaitValue() Assertions.assertEquals(dbList[0], data[0]) } }
方案3:调整Repository的Flow转换逻辑(可选)
若业务逻辑允许,可修改asFlow()的调用,指定不依赖主线程的调度器,但需评估是否符合生产环境的线程约束:
override suspend fun getDataListFromDbAsync(): Flow<List<Item>> = dao.getDataListAsync().asFlow(standardCoroutineDispatcher)
推荐方案
优先选择方案1,它能在不改动业务代码的前提下,让测试环境满足LiveData的线程检查要求,保证测试逻辑与生产逻辑一致。
内容的提问来源于stack exchange,提问作者Sagar Patel
相关产品推荐
相关产品推荐

