单元测试:调用 Notification.setLatestEventInfo() 时出现 NoSuchMethodError
Unit testing: NoSuchMethodError while calling Notification.setLatestEventInfo()
Feel free to improve the title I'm a little uncreative in this special case.
我正在实施一个检查通知的单元测试,我知道这几乎不可能,但我想检查一下我能将它自动化到什么程度。
我无法测试这行简单的代码:
Notification test = new NotificationCompat.Builder(context).build();
原因很愚蠢,也很简单。此处的这段代码将在内部执行:
public Notification build(Builder b, BuilderExtender extender) {
Notification result = b.mNotification;
result.setLatestEventInfo(b.mContext, b.mContentTitle,
b.mContentText, b.mContentIntent);
[...]
我遇到了这个异常:
Caused by: java.lang.NoSuchMethodError: android.app.Notification.setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
at android.support.v4.app.NotificationCompat$NotificationCompatImplBase.build(NotificationCompat.java:479)
at android.support.v4.app.NotificationCompat$Builder.build(NotificationCompat.java:1561)
不难猜测 Google 家伙在这里调用了一个方法,该方法已在 Android Marshmallow SDK 中删除(或更恰当地用 @hide
注释)。我已经确认 call 缺少最新的文档,但它是在 API 1 AFIK.
中引入的
我该如何解决这个问题?
我尝试过但卡住了的事情:
- 覆盖回调,并在不调用该调用的情况下模拟该方法:
我设法通过回调获得 Class<?>
,但是我可以使用哪种方法覆盖 hole 方法?我的意思是我需要修补调用它我不能只是模拟它。
- 正在注入该调用,但是如何注入?我可以覆盖它而不添加它。
抑制调用:
PowerMockito.spy(Notification.class);
PowerMockito.suppress(PowerMockito.method(Notification.class, "setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class));
因为我尝试踢一个不存在的方法,所以以太币不起作用。
- 更改此测试的目标SDK,但我该怎么做?
解决方案比预期的要容易。我错过了默认情况下 Build.VERSION.SDK_INT
的值 0
因为它无法读取实际值。因此支持库仅在存在此方法的平台上调用它。
在 this answer 的帮助下。我只需要添加这段代码:
setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 23);
我的代码有效。
好吧,它仍然在其他地方崩溃,但已创建通知。哇哦!
实际函数:
public static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException, NoSuchFieldException
{
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
Feel free to improve the title I'm a little uncreative in this special case.
我正在实施一个检查通知的单元测试,我知道这几乎不可能,但我想检查一下我能将它自动化到什么程度。
我无法测试这行简单的代码:
Notification test = new NotificationCompat.Builder(context).build();
原因很愚蠢,也很简单。此处的这段代码将在内部执行:
public Notification build(Builder b, BuilderExtender extender) {
Notification result = b.mNotification;
result.setLatestEventInfo(b.mContext, b.mContentTitle,
b.mContentText, b.mContentIntent);
[...]
我遇到了这个异常:
Caused by: java.lang.NoSuchMethodError: android.app.Notification.setLatestEventInfo(Landroid/content/Context;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/app/PendingIntent;)V
at android.support.v4.app.NotificationCompat$NotificationCompatImplBase.build(NotificationCompat.java:479)
at android.support.v4.app.NotificationCompat$Builder.build(NotificationCompat.java:1561)
不难猜测 Google 家伙在这里调用了一个方法,该方法已在 Android Marshmallow SDK 中删除(或更恰当地用 @hide
注释)。我已经确认 call 缺少最新的文档,但它是在 API 1 AFIK.
我该如何解决这个问题?
我尝试过但卡住了的事情:
- 覆盖回调,并在不调用该调用的情况下模拟该方法:
我设法通过回调获得Class<?>
,但是我可以使用哪种方法覆盖 hole 方法?我的意思是我需要修补调用它我不能只是模拟它。 - 正在注入该调用,但是如何注入?我可以覆盖它而不添加它。
抑制调用:
PowerMockito.spy(Notification.class); PowerMockito.suppress(PowerMockito.method(Notification.class, "setLatestEventInfo", Context.class, CharSequence.class, CharSequence.class, PendingIntent.class));
因为我尝试踢一个不存在的方法,所以以太币不起作用。
- 更改此测试的目标SDK,但我该怎么做?
解决方案比预期的要容易。我错过了默认情况下 Build.VERSION.SDK_INT
的值 0
因为它无法读取实际值。因此支持库仅在存在此方法的平台上调用它。
在 this answer 的帮助下。我只需要添加这段代码:
setFinalStatic(Build.VERSION.class.getDeclaredField("SDK_INT"), 23);
我的代码有效。
好吧,它仍然在其他地方崩溃,但已创建通知。哇哦!
实际函数:
public static void setFinalStatic(Field field, Object newValue) throws IllegalAccessException, NoSuchFieldException
{
field.setAccessible(true);
// remove final modifier from field
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}