From 69c8774a16b66b153c89ecf12df015c0e0d8360a Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 14 Sep 2025 10:51:28 +0300 Subject: [PATCH] Add tests for notification components --- app/build.gradle | 2 + .../AppUpdateNotificationsManagerTest.java | 58 +++++++++++++++++++ .../AppUsageNotificationsManagerTest.java | 54 +++++++++++++++++ .../AppUsageNotificationReceiverTest.java | 39 +++++++++++++ .../AppUsageNotificationWorkerTest.java | 48 +++++++++++++++ 5 files changed, 201 insertions(+) create mode 100644 app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUpdateNotificationsManagerTest.java create mode 100644 app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUsageNotificationsManagerTest.java create mode 100644 app/src/test/java/com/d4rk/androidtutorials/java/notifications/receivers/AppUsageNotificationReceiverTest.java create mode 100644 app/src/test/java/com/d4rk/androidtutorials/java/notifications/workers/AppUsageNotificationWorkerTest.java diff --git a/app/build.gradle b/app/build.gradle index 2b07429b..2897f13f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -107,4 +107,6 @@ dependencies { testImplementation libs.androidx.core.testing testImplementation libs.mockito.core testImplementation libs.mockito.inline + testImplementation 'org.robolectric:robolectric:4.10.3' + testImplementation 'androidx.work:work-testing:2.9.0' } \ No newline at end of file diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUpdateNotificationsManagerTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUpdateNotificationsManagerTest.java new file mode 100644 index 00000000..63b995fa --- /dev/null +++ b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUpdateNotificationsManagerTest.java @@ -0,0 +1,58 @@ +package com.d4rk.androidtutorials.java.notifications.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import android.app.NotificationManager; +import android.content.Context; +import android.os.Build; + +import androidx.test.core.app.ApplicationProvider; + +import com.google.android.gms.tasks.Task; +import com.google.android.gms.tasks.Tasks; +import com.google.android.play.core.appupdate.AppUpdateInfo; +import com.google.android.play.core.appupdate.AppUpdateManager; +import com.google.android.play.core.appupdate.AppUpdateManagerFactory; +import com.google.android.play.core.install.model.AppUpdateType; +import com.google.android.play.core.install.model.UpdateAvailability; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowNotificationManager; +import org.robolectric.Shadows; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Build.VERSION_CODES.O) +public class AppUpdateNotificationsManagerTest { + + @Test + public void checkAndSendUpdateNotification_createsChannelAndNotification() { + Context context = ApplicationProvider.getApplicationContext(); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + ShadowNotificationManager shadowNotificationManager = Shadows.shadowOf(notificationManager); + + AppUpdateManager mockAppUpdateManager = mock(AppUpdateManager.class); + AppUpdateInfo mockAppUpdateInfo = mock(AppUpdateInfo.class); + Task task = Tasks.forResult(mockAppUpdateInfo); + when(mockAppUpdateManager.getAppUpdateInfo()).thenReturn(task); + when(mockAppUpdateInfo.updateAvailability()).thenReturn(UpdateAvailability.UPDATE_AVAILABLE); + when(mockAppUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)).thenReturn(true); + + try (MockedStatic mockedStatic = Mockito.mockStatic(AppUpdateManagerFactory.class)) { + mockedStatic.when(() -> AppUpdateManagerFactory.create(context)).thenReturn(mockAppUpdateManager); + + AppUpdateNotificationsManager manager = new AppUpdateNotificationsManager(context); + manager.checkAndSendUpdateNotification(); + + assertNotNull(shadowNotificationManager.getNotificationChannel("update_channel")); + assertEquals(1, shadowNotificationManager.getAllNotifications().size()); + } + } +} diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUsageNotificationsManagerTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUsageNotificationsManagerTest.java new file mode 100644 index 00000000..e0532b8b --- /dev/null +++ b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/managers/AppUsageNotificationsManagerTest.java @@ -0,0 +1,54 @@ +package com.d4rk.androidtutorials.java.notifications.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.app.AlarmManager; +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowAlarmManager; +import org.robolectric.shadows.ShadowPendingIntent; +import org.robolectric.shadows.ShadowSystemClock; +import org.robolectric.Shadows; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +import android.app.PendingIntent; + +import com.d4rk.androidtutorials.java.notifications.receivers.AppUsageNotificationReceiver; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 26) +public class AppUsageNotificationsManagerTest { + + @Test + public void scheduleAppUsageCheck_setsRepeatingAlarm() { + Context context = ApplicationProvider.getApplicationContext(); + long now = 1_000L; + ShadowSystemClock.setCurrentTimeMillis(now); + + AppUsageNotificationsManager manager = new AppUsageNotificationsManager(context); + manager.scheduleAppUsageCheck(); + + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + ShadowAlarmManager shadowAlarmManager = Shadows.shadowOf(alarmManager); + List alarms = shadowAlarmManager.getScheduledAlarms(); + assertEquals(1, alarms.size()); + ShadowAlarmManager.ScheduledAlarm alarm = alarms.get(0); + + assertEquals(AlarmManager.RTC_WAKEUP, alarm.type); + assertEquals(now + TimeUnit.DAYS.toMillis(3), alarm.triggerAtTime); + assertEquals(TimeUnit.DAYS.toMillis(3), alarm.interval); + + PendingIntent pendingIntent = alarm.operation; + ShadowPendingIntent shadowPendingIntent = Shadows.shadowOf(pendingIntent); + assertEquals(AppUsageNotificationReceiver.class.getName(), shadowPendingIntent.getSavedIntent().getComponent().getClassName()); + } +} diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/notifications/receivers/AppUsageNotificationReceiverTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/receivers/AppUsageNotificationReceiverTest.java new file mode 100644 index 00000000..faa0232d --- /dev/null +++ b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/receivers/AppUsageNotificationReceiverTest.java @@ -0,0 +1,39 @@ +package com.d4rk.androidtutorials.java.notifications.receivers; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.content.Intent; + +import androidx.test.core.app.ApplicationProvider; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedStatic; +import org.mockito.Mockito; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = 26) +public class AppUsageNotificationReceiverTest { + + @Test + public void onReceive_enqueuesWork() { + Context context = ApplicationProvider.getApplicationContext(); + WorkManager mockManager = mock(WorkManager.class); + + try (MockedStatic mockedStatic = Mockito.mockStatic(WorkManager.class)) { + mockedStatic.when(() -> WorkManager.getInstance(context)).thenReturn(mockManager); + + AppUsageNotificationReceiver receiver = new AppUsageNotificationReceiver(); + receiver.onReceive(context, new Intent()); + + verify(mockManager).enqueue(any(OneTimeWorkRequest.class)); + } + } +} diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/notifications/workers/AppUsageNotificationWorkerTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/workers/AppUsageNotificationWorkerTest.java new file mode 100644 index 00000000..025a86ae --- /dev/null +++ b/app/src/test/java/com/d4rk/androidtutorials/java/notifications/workers/AppUsageNotificationWorkerTest.java @@ -0,0 +1,48 @@ +package com.d4rk.androidtutorials.java.notifications.workers; + +import static org.junit.Assert.assertEquals; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; + +import androidx.test.core.app.ApplicationProvider; +import androidx.preference.PreferenceManager; +import androidx.work.ListenableWorker; +import androidx.work.testing.TestListenableWorkerBuilder; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowNotificationManager; +import org.robolectric.shadows.ShadowSystemClock; +import org.robolectric.Shadows; + +import java.util.concurrent.TimeUnit; + +@RunWith(RobolectricTestRunner.class) +@Config(sdk = Build.VERSION_CODES.O) +public class AppUsageNotificationWorkerTest { + + @Test + public void doWork_postsNotificationAndUpdatesTimestamp() { + Context context = ApplicationProvider.getApplicationContext(); + long now = 10_000L; + ShadowSystemClock.setCurrentTimeMillis(now); + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + prefs.edit().putLong("lastUsed", now - TimeUnit.DAYS.toMillis(4)).apply(); + + AppUsageNotificationWorker worker = TestListenableWorkerBuilder.from(context, AppUsageNotificationWorker.class).build(); + ListenableWorker.Result result = worker.doWork(); + assertEquals(ListenableWorker.Result.success(), result); + + NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + ShadowNotificationManager shadowNm = Shadows.shadowOf(nm); + assertEquals(1, shadowNm.getAllNotifications().size()); + + assertEquals(now, prefs.getLong("lastUsed", 0)); + } +}