如何在 kotlin 中使用 ProviderTestRule 测试 ContentProvider

问题描述

我已经实现了一个使用 Room 数据库来存储数据的 ContentProvider。实现是在 kotlin 中完成的,它遵循 Google example显示的相同模式。 ContentProvider 在应用程序中使用时工作正常。现在我想写一些测试,我依靠 ProviderTestRule 来做这件事。我的配置看起来不错,但不幸的是我收到以下异常,看起来缺少一些初始化,然后上下文不可用。

java.lang.UnsupportedOperationException
at androidx.test.rule.provider.DelegatingContext.getSystemService(DelegatingContext.java:277)
at androidx.room.RoomDatabase$JournalMode.resolve(RoomDatabase.java:517)
at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:943)

我找不到有关如何测试此方案的任何示例。任何提示都会非常有帮助!

解决方法

ProviderTestRule 在内部使用 DelegatingContext,它是应用程序上下文的包装器,有意限制其功能。

从源代码中您可以看到 context.getSystemService 被剔除,大部分时间都抛出 UnsupportedOperationException

  /**
   * This method only supports retrieving {@link android.app.AppOpsManager},which is needed by
   * {@link android.content.ContentProvider#attachInfo}.
   */
  @Override
  public Object getSystemService(@NonNull String name) {
    checkArgument(!TextUtils.isEmpty(name),"name cannot be empty or null");
    // getSystemService(Context.APP_OPS_SERVICE) is only used in ContentProvider#attachInfo for
    // API level >= 19.
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
        && Context.APP_OPS_SERVICE.equals(name)) {
      return context.getSystemService(Context.APP_OPS_SERVICE);
    }
    throw new UnsupportedOperationException();
  }

我没有明确解释为什么他们首先禁止 ProviderTestRule 访问系统服务。 不幸的是,Room 似乎需要访问 ActivityManager 才能找到最合适的 JournalMode

您可以尝试解决这种情况的方法:

  1. 强制您房间数据库的 JournalModeJournalMode.WRITE_AHEAD_LOGGING(或 JournalMode.TRUNCATE),或
  2. 如果它没有解决问题,您必须编写自己的 ProviderTestRule,使用真实的应用程序上下文并允许访问所需的系统服务。