【问题标题】:Creating notification from InstrumentationTestCase从 InstrumentationTestCase 创建通知
【发布时间】:2016-03-17 05:18:41
【问题描述】:

我想通过单元测试来测试通知是否能够从资产中播放自定义声音。 该测试并不是为了验证任何东西,我编写它是为了快速演示一项功能,而不会弄乱主应用程序代码。

所以在测试项目中,我在/res/raw里面添加了一个wav文件。我将在通知生成器中使用此 URL:

Uri path = Uri.parse("android.resource://<main app package name>/testsound.wav");

该 URL 应该根据我在 SO 中阅读的问题起作用。让我们假设它有效。

现在因为我不想将测试 wav 文件包含在主项目的 /res/raw 文件夹中,但在测试项目之一中,我不得不让我的单元测试从 InstrumentationTestCase 扩展,以便我可以访问测试项目中的资源。

代码如下:

    NotificationCompat.Builder builder = new NotificationCompat.Builder(getInstrumentation().getContext());
    ...
    builder.setSound(path, AudioManager.STREAM_NOTIFICATION);
    ...
    NotificationManager notificationManager = (NotificationManager) getInstrumentation().getContext().getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(NOTIFICATION_ID, builder.build());

notify 调用引发以下异常:

    java.lang.SecurityException: Calling uid 10198 gave package <main app package name> which is owned by uid 10199
    at android.os.Parcel.readException(Parcel.java:1540)
    at android.os.Parcel.readException(Parcel.java:1493)
    at android.app.INotificationManager$Stub$Proxy.enqueueNotificationWithTag(INotificationManager.java:611)
    at android.app.NotificationManager.notify(NotificationManager.java:187)
    at android.app.NotificationManager.notify(NotificationManager.java:140)
    ... 
    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1873)

我已将此异常跟踪到 NotificationManagerService 类:

    void checkCallerIsSystemOrSameApp(String pkg) {
        int uid = Binder.getCallingUid();
        if (UserHandle.getAppId(uid) == Process.SYSTEM_UID || uid == 0) {
            return;
        }
        try {
            ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
                    pkg, 0, UserHandle.getCallingUserId());
            if (!UserHandle.isSameApp(ai.uid, uid)) {
                throw new SecurityException("Calling uid " + uid + " gave package"
                        + pkg + " which is owned by uid " + ai.uid);
            }
        } catch (RemoteException re) {
            throw new SecurityException("Unknown package " + pkg + "\n" + re);
        }
    }

显然,该异常与自定义声音没有任何关系,而是因为我们正在从 InstrumentationTestCase 创建通知。

有没有办法测试这个?我记得过去从AndroidTestCase 创建了通知,但如果我这样做了,我将无法访问测试 wav 文件。我可以用 wav 创建一个 jar 并将 jar 放到测试项目的 lib 文件夹中,但这会隐藏文件,如果将来需要替换它,其他程序员可能很难找到它。

【问题讨论】:

    标签: java android unit-testing notifications android-instrumentation


    【解决方案1】:

    实际上,我对这个问题有点困惑。所以我写了一个小的 Instrumentation 测试。

    为了断言,我将测试标记为仅在 API 23 上运行(getActiveNotifications 之前似乎不可用),但它在旧 API 上也能正常运行。

    诀窍是使用getTargetContext() 而不是getContext()

    public final class MainActivityTest extends ActivityUnitTestCase<MainActivity> {
    
        public MainActivityTest() {
            super(MainActivity.class);
        }
    
        @TargetApi(Build.VERSION_CODES.M)
        public void testSendNotification() {
            final NotificationManager manager = (NotificationManager) getInstrumentation().getTargetContext().getSystemService(Context.NOTIFICATION_SERVICE);
    
            manager.cancel(42);
            assertEquals(0, manager.getActiveNotifications().length);
    
            final NotificationCompat.Builder builder = new NotificationCompat.Builder(getInstrumentation().getTargetContext());
            builder.setContentTitle("Notification test")
                    .setAutoCancel(true)
                    .setContentText("Hello, Mister Smith")
                    .setSmallIcon(R.drawable.ic_launcher_notification);
    
            manager.notify(42, builder.build());
    
            assertEquals(1, manager.getActiveNotifications().length);
        }
    }
    

    它就像一个魅力:

    希望,它会有所帮助。

    【讨论】:

    • 是的,这行得通。 wav 文件按预期播放。唯一的缺点是您需要主项目中的活动。
    • @MisterSmith 但您一直在询问仪器测试,而不是单元测试。您的回答不反映您提出的问题。
    • 是的,很抱歉。我假设为了访问测试项目中的资源,我必须使用仪器,如this question 中所述。但事实证明它可以通过常规单元测试工作。还是谢谢。
    【解决方案2】:

    我想出了如何让声音从AndroidTestCase 工作。 wav 文件已添加到测试项目的原始文件夹中,并且未按预期包含在主项目中。

    通知的 URL 是这样的:

    Uri path = Uri.parse("android.resource://<test project package name>/" + R.raw.air_horn);
    

    生成器的获取方式如下:

    NotificationCompat.Builder builder = new NotificationCompat.Builder(getContext());
    

    【讨论】:

      猜你喜欢
      • 2018-03-19
      • 2021-06-17
      • 2014-03-06
      • 1970-01-01
      • 2018-06-20
      • 2022-01-03
      • 1970-01-01
      • 1970-01-01
      • 2023-04-09
      相关资源
      最近更新 更多