rekire rekire - 4 months ago 35x
Android Question

Unit testing: NoSuchMethodError while calling Notification.setLatestEventInfo()

Feel free to improve the title I'm a little uncreative in this special case.

I am implementing a unit test for checking notifications, this is hardly possible I know, but I want to check how far I can automatism it.

I cannot test this simple line of code:

Notification test = new NotificationCompat.Builder(context).build();

The reason is stupid and simple in one. This code here will been executed internally:

public Notification build(Builder b, BuilderExtender extender) {
Notification result = b.mNotification;
result.setLatestEventInfo(b.mContext, b.mContentTitle,
b.mContentText, b.mContentIntent);

I'm getting this exception:

Caused by: java.lang.NoSuchMethodError:;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V

It is not hard to guess that the Google guys call here a method which was removed (or more properly annotated with
) in the Android Marshmallow SDK. I have verified it that call is missing the the newest documentation, but it was introduced in API 1 AFIK.

How can I work around that?

Things I tried and got stuck:

  • Overriding the callback, and mock that method without invoking that call:

    I got it managed to get that
    with the callback, but with which method I can override a hole method? I mean I need to patch the call it I cannot just mock it.

  • Injecting that call, but how? I can just override it and not adding it.

  • Suppressing the call with:

    PowerMockito.suppress(PowerMockito.method(Notification.class, "setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class));

    Does not work ether since I try to kick a non existing method.

  • Change the target SDK for this test, but how can I do it?


The solution is easier than expected. I missed that by default Build.VERSION.SDK_INT has the value 0 since it cannot read the real value. So that support library calls it on just that platforms where this method exists.

With the help of this answer. I just had to add this code:

setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 23);

And my the codes works.
Well it still crashes somewhere else, but the notification is created. Wohoo!

And the actual function:

public static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException, NoSuchFieldException
    // remove final modifier from field
    Field modifiersField = Field.class.getDeclaredField("modifiers");
    modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
    field.set(null, newValue);