从InstrumentationTestCase创建通知

11

我希望能够从单元测试中测试通知是否能够播放来自资产的自定义声音。

该测试并不旨在验证任何内容,我编写它只是为了快速展示一个功能而不会使主应用程序代码混乱。

因此,在测试项目中,我在/res/raw中添加了一个wav文件。 我将使用此URL与通知构建器:

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

根据我在SO上读到的问题,那个URL应该是有效的。假设它有效。

现在由于我不想将测试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,并将其放入测试项目的lib文件夹中,但这将隐藏该文件,其他程序员在未来需要替换该文件时可能会很难找到它。

2个回答

2

实际上,我对这个问题有点困惑。因此,我编写了一个小型的仪器测试。

为了进行断言,我将测试标记为仅在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文件按预期播放。唯一的缺点是需要从您的主项目中获取一个活动。 - Mister Smith
@MisterSmith 但是你一直在询问仪器测试,而不是单元测试。你的回答并没有反映出你所问的问题。 - Konstantin Loginov
是的,非常抱歉。我一开始以为要使用 instrumentation 才能访问测试项目中的资源,就像 这个问题 中所描述的那样。但实际上它可以在普通的单元测试中工作。无论如何,还是谢谢你。 - Mister Smith

0

我找到了如何从AndroidTestCase中使声音工作的方法。 Wav文件被添加到测试项目的原始文件夹中,而不是像预期的那样包含在主项目中。

通知的URL是这样的:

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

构建器的获取方式如下:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接