Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Original file line number Diff line number Diff line change
@@ -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<AppUpdateInfo> 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<AppUpdateManagerFactory> 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());
Comment on lines +51 to +55

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Run update notification task before asserting results

The test invokes checkAndSendUpdateNotification() and immediately inspects the shadow NotificationManager, but the method under test wires an OnSuccessListener to a Play Core Task which posts the listener to the main looper. Because the test never drains the main looper, the listener is never executed and no channel or notification is created, causing the assertions to fail. Ensure the test advances the main looper (e.g. Shadows.shadowOf(Looper.getMainLooper()).idle() or supply a direct executor) before asserting the results.

Useful? React with 👍 / 👎.

}
}
}
Original file line number Diff line number Diff line change
@@ -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<ShadowAlarmManager.ScheduledAlarm> 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());
}
}
Original file line number Diff line number Diff line change
@@ -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<WorkManager> 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));
}
}
}
Original file line number Diff line number Diff line change
@@ -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));
}
}
Loading