araki tech

for developers including me

ContentProviderのテストで予期しているExceptionが返らずnullが返ってくる原因

背景

下記のようなContentProvider.queryの形はよく見る。

override fun query(
    uri: Uri,
    projection: Array<out String>?,
    selection: String?,
    selectionArgs: Array<out String>?,
    sortOrder: String?,
): Cursor? {
    return when (uriMatcher.match(uri)) {
        ID_1 -> getSomething1()
        ID_2 -> getSomething2()
        else -> throw IllegalArgumentException("Unknown URI: $uri")
    }
}

上記の処理としては、「ID_1またはID_2に該当するURIが渡されれば適切なCursorを発行して、そうでなければ例外を投げる」というもの。

しかし、「例外をしっかり投げてくれるかのテスト」を作成したときに上手くいかなかったのでメモを残すことにした。

そもそも例外投げるだけのケースをテストする必要があるかどうかは分からないが…。

@Test(expected = IllegalArgumentException::class)
fun test_query_invalidUri() {
    resolver.query(Uri.parse(INVALID_URI), null, null, null, null)
}

companion object {
    private val INVALID_URI = "invalid_uri"
}

ちなみに上記のテストは例外は投げられず代わりにnullqueryから返却される

上記コードはKotlinだがJavaでも同様。

解消方法

結論から言うと、INVALID_URIがURIのフォーマットになっていないからである。

@Test(expected = IllegalArgumentException::class)
fun test_query_invalidUri() {
    resolver.query(Uri.parse(INVALID_URI), null, null, null, null)
}

companion object {
    private val INVALID_URI = "content://tech.araki.provider/invalid_uri"
}

のような形であれば問題なく独自のContentProviderで定義したqueryが呼ばれて、例外を返してくれる。

どうやら、URIのフォーマットが適切でない場合、ContentResolver.query側で早期にnullを返す仕組みになっているらしい。

補足

公式ページにもそれらしき文言が書いてあった。

これはもしかしたら、insertなどの他のメソッドでもURIフォーマットが不適切だったらProviderまで届かないのかもしれない。(未調査)

ContentProvider.query() メソッドは Cursor オブジェクトを返す必要があります。失敗した場合は、Exception をスローします。SQLite データベースをデータ ストレージとして使用する場合は、SQLiteDatabase クラスのいずれかの query() メソッドが返す Cursor を返します。クエリがどの行にも一致しない場合は、getCount() メソッドが 0 を返す Cursor インスタンスを返す必要があります。クエリプロセス中に内部エラーが発生した場合にのみ null を返します。

SQLite データベースをデータ ストレージとして使用しない場合は、Cursor クラスのいずれかの具象サブクラスを使用します。たとえば、MatrixCursor クラスは、各行が Object の配列となるカーソルを実装します。このクラスでは、addRow() を使って新しい行を追加します。

Android システムは、プロセスの境界をまたいで Exception を送信できるように設定する必要があることにご注意ください。Android では、クエリエラーを処理するのに便利な、次の例外を送信できます。

(コンテンツプロバイダの作成 | Android developers #query()メソッドを実装する)