Android中如何通过Unit Test或Instrumented Test测试Alarm Manager
Alarm Manager 闹钟设置校验测试方案
一、单元测试(本地JVM测试,无需真机/模拟器)
单元测试核心思路是Mock系统AlarmManager、Context、PendingIntent等依赖,验证调用参数是否符合预期,这里用MockK作为Mock框架举例:
- 依赖配置(build.gradle.kts)
testImplementation "io.mockk:mockk:1.13.8" testImplementation "junit:junit:4.13.2"
- 测试代码示例
class AlarmSetterTest { private val mockContext = mockk<Context>() private val mockAlarmManager = mockk<AlarmManager>() private val mockPendingIntent = mockk<PendingIntent>() @Before fun setUp() { every { mockContext.getSystemService(Context.ALARM_SERVICE) } returns mockAlarmManager every { PendingIntent.getBroadcast(any(), eq(1), any<Intent>(), eq(0)) } returns mockPendingIntent // 固定当前时间,避免测试受时间影响 mockkStatic(LocalDateTime::class) val fixedNow = LocalDateTime.of(2024, 5, 20, 10, 0, 0) every { LocalDateTime.now() } returns fixedNow } @Test fun `alarm should be set to next day 1:02 AM`() { // 执行给定的闹钟设置逻辑 val alarmManager1 = mockContext.getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmIntent1 = Intent(mockContext, AlarmReceiver::class.java) val requestCode1 = 1 val pendingIntent1 = PendingIntent.getBroadcast(mockContext, requestCode1, alarmIntent1, 0) val calendar: Calendar = Calendar.getInstance() val currentDateTime = LocalDateTime.now() calendar.set( currentDateTime.year, currentDateTime.monthValue - 1, currentDateTime.dayOfMonth + 1, 1, 2, 0 ) alarmManager1.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent1) // 验证参数是否正确 val expectedCalendar = Calendar.getInstance().apply { set(2024, 4, 21, 1, 2, 0) } verify(exactly = 1) { mockAlarmManager.setExactAndAllowWhileIdle( eq(AlarmManager.RTC_WAKEUP), eq(expectedCalendar.timeInMillis), eq(mockPendingIntent) ) } } @After fun tearDown() { unmockkAll() } }
注意:单元测试只能验证你的代码调用逻辑是否正确,无法验证系统层面闹钟是否真的触发,真实触发校验需要用插桩测试
二、插桩测试(需运行在真机/模拟器)
插桩测试可以直接调用系统服务,验证闹钟是否真的被注册到系统中:
- 依赖配置(build.gradle.kts)
androidTestImplementation "androidx.test.ext:junit:1.1.5" androidTestImplementation "androidx.test:core:1.5.0"
- 测试代码示例
@RunWith(AndroidJUnit4::class) class AlarmManagerInstrumentedTest { @Test fun verifyAlarmIsRegistered() { val context = ApplicationProvider.getApplicationContext<Context>() // 先执行给定的闹钟设置代码 val alarmManager1 = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmIntent1 = Intent(context, AlarmReceiver::class.java) val requestCode1 = 1 // 用FLAG_NO_CREATE判断对应PendingIntent是否已经被注册 val pendingIntent1 = PendingIntent.getBroadcast(context, requestCode1, alarmIntent1, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE) // PendingIntent不为空即可证明闹钟已经注册到系统 assertNotNull(pendingIntent1) // Android 12及以上可以通过getNextAlarmClock()验证下一个闹钟的时间是否符合预期 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val nextAlarm = alarmManager1.nextAlarmClock assertNotNull(nextAlarm) } } }
额外修复建议:你提供的代码里PendingIntent的flag用了0,Android 12及以上要求必须指定
PendingIntent.FLAG_IMMUTABLE或者PendingIntent.FLAG_MUTABLE,否则会抛出异常,建议把flag改成PendingIntent.FLAG_IMMUTABLE适配高版本系统。
内容的提问来源于stack exchange,提问作者Muhammad Zaryab Rafique
相关产品推荐
相关产品推荐

