Flutter開発中でのSQFLite(ローカルDB)のテスト方法
私は、Flutterにおけるローカルデータの保存には、SQFLiteパッケージを使用してデータベース操作を行う場合が殆どです。
クエリの実行内容や、トランザクション処理に変更を加えた時などに、いちいち実機を立ち上げてテストするのは非常に面倒ですので、一括でテストが回せたら非常に便利です。私の備忘録として残しておきます。
サンプルコード
テストには、sqfliteに加えて以下のパッケージが必要ですので、インストールしてください。
import 'package:flutter_test/flutter_test.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
void main() {
late Database db;
//テストが始まる前に一度だけ実行される
setUpAll(() {
TestWidgetsFlutterBinding.ensureInitialized();
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
});
//各テストが開始するたびに実行される
setUp(() async {
db = await CreateDatabase().getDatabase();
});
//各テストが終わるたびに実行される
tearDown(() async {
//テストが終わるたびにテスト用DBを削除。
await deleteDatabase(db.path);
});
test('Sqflite test', () async {
//ここにDBのテスト内容を書く
});
}
注目すべきはsetUp()とtearDown()の記述です。
setUp()はテストが開始するたびに、tearDownはテストが終了するたびに実行されます。
このような書き方をすることで、どのテストにおいても独立したデータベースを使用することができ、他のテストへ影響を及ぼさないことに加え、実行順序にも依存しない関係ができるはずです。
Kent Beck氏は著書『テスト駆動開発』にて、次のように述べています。
テストの実行は、他のテストにどのような影響を及ぼすべきか---
決して及ぼすべきではない
『テスト駆動開発』、Kent Beck著; 和田卓人訳 p.189
Debugモードのテストだとデッドロックの可能性がある
もし、DebugモードでDBのテストを実施し、途中でエラーが出てしまう場合(トランザクション内でのエラー)テストが一向に終了せず、無限にグルグルしてしまいます。
こうなってしまうと、次に実行するテスタがDBにアクセスしようとしてデッドロックを引き起こしてしまいます。
なのでタスクマネージャーからキルしてしまいましょう。
ちなみに、エディタソフト(私の場合はVScode)を終了してもタスクマネージャにはプロセスが実行中として表示されていたため、やはりタスクキルするのがいいかと思います。
そもそもデバッグモードではなく、普通にrunさせればこのような問題は起こらず、トランザクションに失敗したらテストも終了します。
ただ、ブレークポイントを使って逐一変数を確認したい場合などは、debugモードを使いたいと思いますので、その場合はこの現象に注意してください。
まとめ
ある程度開発が進んだ後のクエリ編集やDBテーブル変更、制約の追加等は、腰が重くなってしまいがちですが、そうなってしまう理由はテストのしにくさにあると思っています。
DBのCRUD操作を簡単にテストできれば、かなり開発がしやすくなると思います。
Discussion