連載
» 2012年02月24日 00時00分 公開

Androidアプリ開発テスト入門(4):スマホアプリに必須なデータ永続化のためのDBテスト (1/3)

日本Androidの会テスト部が、いままで培ってきたAndroidアプリ開発におけるテストのノウハウを、実際のテストコード例とともに紹介していきます

[吉澤毅,日本Androidの会テスト部]

スマホアプリにデータ永続化が欠かせない理由

 本連載「Androidアプリ開発テスト入門」では、Androidアプリを開発している方のためにテストの基本的なノウハウを解説しています。第4回では、データベース(以下、DB)のテストについて解説します。

 第2回の「Androidでビジネスロジックのテストを自動化するには」でも触れたように、DBのテストはビジネスロジックのテストの一部に含まれます。

 これまでの連載でお伝えしてきた内容では、外部通信のやりとりは発生しませんでしたが、アプリが外部リソースを参照したり更新するようになると、サーバとのやりとりによってネットワーク通信が発生します。

 サーバに毎回リソースの処理を要求する作りになっていると、その都度サーバからの処理待ちが発生します。しかし、一度取得したデータをキャッシュしておいて、再度同じ画面(Activity)を開いたときにはキャッシュしたデータを利用すると、ユーザーは待たされず操作できるアプリを提供できるようになります。

 また、キャッシュだけではなく、アプリで利用するデータを履歴としてクライアントに保存しておきたいこともあります。データを永続化することによって、キャッシュによるメモリ使用量削減といったアプリの性能向上を可能にします。スマートフォンアプリで求められることが多いオフライン対応が行えるようにもなります。

データ永続化のためのDBテストの必要性

 Androidには、データを永続化する手段として、設定情報を保存するPreferenceファイル入出力、それからSQLiteを利用したDBの3つがあります。

 画像ファイルをローカルにキャッシュしてユーザーへ早くレスポンスを返す場合は、画像データをファイルとして保存しておくことで解決できますが、データに構造を持たせてアプリで多様な処理を行う場合にはSQLを利用できるDBを選択することになるでしょう。

 SQLiteにおけるDB操作はSQLを記述して、追加、検索、更新、削除のCRUD処理を行えます。データを取り扱う場合には、さまざまな条件を指定しますが、条件を誤ると正しくデータを表示できなかったり、場合によってはデータを破壊してしまいます。

 また、DBの構造が変わることになった場合には、データを問題なく移行できることを確認し、かつ、これまで実装した他のデータ操作に影響がないか検証することが重要になってきます。

 そこで今回の記事では、次の問題を解決できるようにします。

  1. SQLiteへのデータ処理テスト
  2. マイグレーションテスト

 AndroidアプリのSQLiteの操作の基本について知りたい方は、以下の記事を参照してください。

AndroidでSQLiteのDB操作をするための基礎知識
Androidで動く携帯Javaアプリ作成入門(6)ついに日本でもドコモからHTC製端末の発売が決定したAndroid。今回はデータベースを操作するための基礎を解説します
Smart & Social」フォーラム 2009/5/20

データアクセス層を分離して疎結合にしておこう

 早速、AndroidにおけるDBへのテスト方法について述べたいところですが、その前にレイヤの切り分けについてお話します。

なぜ、疎結合が良いのか

 DBアクセスのロジックがActivityクラスに直接記述されているような密結合な状態だと、「DBのテストを行うはずがUIの処理に引きずられテストコードが書きづらい」「テストができない」という状況に陥ります。ここをきちんと切り分けて疎結合な関係にしておくことで、DBのテストに集中できるようになります。

 さらに、「メンテナンスのしやすい保守性のあるコードが生まれる」といううれしい副作用が生じます。

 また、ここの分離がうまくできている(依存関係が少ない)とモックオブジェクトへの適用もスムーズに進みます。なおモックオブジェクトに関しては、次の連載第5回で話をします。

 データアクセス層のドメインモデルに関してはさまざまあり、それぞれメリット、デメリットがあり議論されています。今回はJavaのWebアプリでよく用いられるDAO(Data Access Object)パターンで説明を進めますが、必ずしもこのパターンがAndroidのデータアクセス層として適しているとはいえないことに注意してください。

コラム アプリに適したパターンを使おう

例えば、Android SDKの「android.content.ContentProvider」クラスのDB選択処理では、「android.database.Cursor」のカーソルを返しています。これはサーバプログラミングとは違い、一覧データに対して逐次、処理を行い、クライアントにレスポンスを早く返すことや、メモリ使用量を抑えたり、抽象化を考慮しているためでしょう。

カーソルでは扱いづらいので、1件ずつモデルに変換したものを返すようなコールバックインターフェイスに提供することもあるでしょうが、どういったパターンを採用することになっても大きくテスト手法が変わるわけではありません。実装の際にはアプリに適したパターンを採用してください。


DAOパターンのコード例

以下は、作成したDAOを介してUserモデルをActivityの初期起動時に表示するときのコード例です。

public class SQLiteUserDao implements UserDao {
    
    private SQLiteDatabase db;
 
    public SQLiteUserDao(SQLiteDatabase db) {
        this.db = db;
    }
 
    @Override
    public List<User> findByName(String name) {
        List<User> users = new ArrayList<User>();
        Cursor cursor =
            db.rawQuery(
                "select _id, name, image, createdAt, updatedAt from user where name like ? escape '$' order by _id",
                new String[] { "%" + name.replaceAll("[$_%]", "\\$$0") + "%" });
        try {
            while (cursor.moveToNext()) {
                User user = toUser(cursor);
                users.add(user);
            }
            return users;
        } finally {
            cursor.close();
        }
    }
 
    protected User toUser(Cursor cursor) {
        int col = 0;
        User user = new User();
        user.setId(cursor.getInt(col++));
        user.setName(cursor.getString(col++));
        user.setAddress(cursor.getString(col++));
        user.setCreatedAt(new Date(cursor.getLong(col++)));
        user.setUpdatedAt(new Date(cursor.getLong(col++)));
 
        return user;
    }
}

 DBと直接やりとりしている個所をActivityから分離できるようになりました。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    AppOpenHelper helper = new AppOpenHelper(getBaseContext());
    try {
        SQLiteDatabase db = helper.getReadableDatabase();
        try {
            UserDao userDao = new SQLiteUserDao(db);
            List<User> users = userDao.findByName("□");
            // ArrayAdapter にセット
// …省略…
        } finally {
            db.close();
        }
    } finally {
        helper.close();
    }
}

 次ページでは、テスト専用DBを作成してテストを行います。

       1|2|3 次のページへ

Copyright © ITmedia, Inc. All Rights Reserved.

編集部からのお知らせ

8月8日10時30分〜16時30分の間、システムメンテナンスのため記事の一部表示や資料のダウンロードができなくなります。ご理解のほどよろしくお願いいたします。

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。