Android開発でテスト用データをロードする

Android プログラムでデータベースを利用したアプリケーションを作成する場合、リリースするプログラムではだいたいデータのない状態がスタートなんですが、開発中はそれでは面倒くさい。

いったんアプリケーションをエミュレータにインストールすれば、実行するたびにDBの内容は消えたりしないんだけど、何せ開発中なのでテーブル構造もちょいちょい変わるし、バグがあっておかしなデータを保存することもあるので、「テーブルを消して、テストデータをロード」を簡単にしたい。

どうも Android にはその辺の仕組みが備わってはいないようだ。
Rails みたいに、スキーマを変えたら rake db:migrate、初期値データのロードは rake db:seed とかできたら素敵なんだけど。

まあ、そこまで頑張るのは大変なので適当な対処を以下のようにしている

1.初期データ投入用のスクリプトを適当に scripts ディレクトリなどに作成する

もちろん、scripts ディレクトリなどプロジェクトの標準ディレクトリ構造にはないので自分で mkdir する。

2.適当に setup_tables.sh なる sh スクリプトを書く

adb shell には既に /system/bin/sh が用意されているのでこれを利用しよう。
bash を追加で入れたりもできるようだけど、面倒なので今はパス。

#!/system/bin/sh

sqlite3 pwr.db "DELETE FROM tableA;"
sqlite3 pwr.db "DELETE FROM tableB;"

sqlite3 pwr.db <<EOF
INSERT INTO tableA (_id, col1, col2) VALUES (1, 'A01', 'ほげ');
INSERT INTO tableA (_id, col1, col2) VALUES (1, 'A02', 'ぷー');
EOF

sqlite3 pwr.db <<EOF
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (1, 'はげ', 1, strftime('%s', datetime('now', '-1days'), 'utc') * 1000);
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (2, 'ぱげ', 1, strftime('%s', datetime('now', '-1days'), 'utc') * 1000);
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (3, 'ぷげ', 1, strftime('%s', datetime('now', '-1days'), 'utc') * 1000);
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (4, 'ぴー', 2, strftime('%s', datetime('now'), 'utc') * 1000);
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (5, 'ぽー', 2, strftime('%s', datetime('now'), 'utc') * 1000);
INSERT INTO tableB (_id, 'col3', 'f_key', create_at)
       VALUES (6, 'ぱー', 2, strftime('%s', datetime('now'), 'utc') * 1000);
EOF

などと適当に書いて、これを setup_tables.sh などという名前で scrpits ディレクトリに保存。
SQLite3 には DATE タイプとかないので、1970/1/1 からの秒数を日付として処理。しかし Java は 1970/1/1 からのミリ秒数で日付を保持するのでいちいち 1000 で掛けたり割ったりせねばらん。

3.作成したスクリプトエミュレータにインストール

エミュレータを起動した状態で、

$ adb push scripts/setup_tables.sh /data/data/YOUR_PACKAGE_NAME/databases/

などとする。
YOUR_PACKAGE_NAME の所は作成しているプログラムのパッケージ名を、インストールする場所もどこでもいいんだけど db ファイルのある場所と同じにしておけばいいか。

4.実行

特に説明不要、adb shell から、databases の場所まで移動して

# sh setup_tables.sh

とするだけ。

5.必要なら表示するスクリプトも書く

アプリケーション実行の結果、テーブルに保存された内容をちょいちょい見たいので、
SELECT * ... するだけのスクリプトも書いた

#!/system/bin/sh

MESSAGE="a)tableA, b)tableB, q)quit"

echo $MESSAGE
echo
while read TABLE_NAME; do
    case $TABLE_NAME in
        a|tableA)
            sqlite3 -header -column YOUR_DB_NAME.db "SELECT * FROM tableA;"
            ;;
        b|tableB)
            sqlite3 -header -column YOUR_DB_NAME.db "SELECT _id, \
                                                            col1, \
                                                            col2, \
                                                            a_id, \
                                                            datetime(created_at / 1000, 'unixepoch', 'localtime') \
                                                            'created_at' \
                                                       FROM tableB OUTER LEFT JOIN tableA \
                                                         ON f_key = tableA._id;"
            ;;
        q|quit)
            break
            ;;
        *) echo "you must specify a table name"
            ;;
    esac

    echo $MESSAGE
    echo
done
exit 0

これを display_tables.sh という名前で scripts ディレクトリに作成。 /data/data/YOUR_PACKAGE_NAME/databases/ の下にインストールして、

# sh display_tables.sh

などとすると、開発中にそのターミナルで a とか b とか入れると内容を都度確認できる。sh と入力するのが面倒な人は

# chmod 766 display_tables.sh

すると ./display_tables.sh で実行できるようになるけど、タイプ数としてはあんまり変わらん。