android app, a java class needs to do something based on the NotificationManager's status.
class Util {
static void setupByPermission(@NonNull final Context appContext) {
Thread t = new Thread(new Runnable() {
public void run() {
try {
NotificationManagerCompat nm = NotificationManagerCompat.from(appContext); // should got from stub
boolean overallPermission = currentNotificationsPermission(nm);
if (overallPermission) {
doWithPermission();
} else {
doWithoutPermission();
}
} catch (Throwable ex) {}
}
});
t.start();
}
static boolean currentNotificationsPermission(@NonNull NotificationManagerCompat nm) {
System.out.println("+++ enter currentNotificationsPermission("+nm+")");
boolean overallPermission = nm.areNotificationsEnabled();// should got result from stub
System.out.println("+++ ========= in currentNotificationsPermission("+nm+"), nm.areNotificationsEnabled() ==> "+overallPermission);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (overallPermission) {
List<NotificationChannel> channels = nm.getNotificationChannels();
boolean someChannelEnabled = channels.isEmpty();
for (NotificationChannel channel : channels) {
if (channel.getImportance() != NotificationManagerCompat.IMPORTANCE_NONE) {
someChannelEnabled = true;
break;
}
}
overallPermission = overallPermission && someChannelEnabled;
}
}
System.out.println("+++ --- exit =========== currentNotificationsPermission(), overallPermission:"+overallPermission);
return overallPermission;
}
}
would like to stub the NotificationManagerCompat.areNotificationsEnabled()
to force a test with the return of either true or false.
test using mockito-inline 3.8.0
@Test
public void test () throws Exception {
try (MockedStatic<NotificationManagerCompat> nmMoc = Mockito.mockStatic(NotificationManagerCompat.class);
MockedStatic<Util> utilMoc = Mockito.mockStatic(Util.class)
) {
NotificationManagerCompat nmSpy = spy(NotificationManagerCompat.from(application));
when(nmSpy.areNotificationsEnabled())
.thenReturn(false); //or true
nmMoc.when(() -> NotificationManagerCompat.from(any(Context.class)))
.thenReturn(nmSpy);
// test
final CountDownLatch latch = new CountDownLatch(1);
utilMoc.setupByPermission(application);
latch.await(2, TimeUnit.SECONDS);
Mockito.verify(......);
}
}
but the stub is not called when they are in the Thread. same if stub the currentNotificationsPermission().
Hot to make the stub for a static function to work in the the thread?
There seem to be a few things wrong with your test:
MockedStatic. It explicitly tells you that the mock works only on the original thread.utilMoc is a MockedStatic<Util> so how can you call setupByPermission on it?Util, calling methods on Util will not call the 'real' method anyway, unless you tell it to. See the example below.Runnable. In your tests use a dummy implementation which runs the Runnable on the current thread.import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
public class TestExample {
public static class Foo {
public static String bar() {
return "bar";
}
}
@Test
public void aTest() {
try (MockedStatic<Foo> foo = Mockito.mockStatic(Foo.class)) {
// without this line Foo.bar() will return null
when(Foo.bar()).thenCallRealMethod();
assertThat(Foo.bar()).isEqualTo("bar");
}
}
}
While people have put in a lot of work to make it possible to mock static methods, as you're seeing it still isn't straightforward. The refactoring needed to avoid it is straightforward and has other benefits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With