More Related Content
Similar to Flutterで単体テストを行う方法とGitHub Actionsを使った自動化
Similar to Flutterで単体テストを行う方法とGitHub Actionsを使った自動化 (20)
Flutterで単体テストを行う方法とGitHub Actionsを使った自動化
- 4. 品質向上とリリースのスピードはトレードオフ
「Done is better than perfect」 → 完璧を目指すよりもまず終わらせろ
とはいえ全く動かさずにリリースするわけにはいかない
品質の落としどころ
・ベータ版としてリリース → ユーザにテストしてもらう
・モンキーテスト → テストケースを決めずに適当にクリックしたり入力したりしてみる
選択肢として
ビジネスロジックの自動テストを考えてみる
- 9. Unitテストの書き方と実行方法
プロジェクトルートの
testフォルダの下に
XXX_test.dartファイルを作成
import 'package:flutter_test/flutter_test.dart' ;
import
'package:todo_app_sample_flutter/data/todo_item.dart' ;
void main() {
group('TodoItemのゲッターのテスト ', () {
final TodoItem todoItem = TodoItem(
id: 0,
title: 'title',
body: 'body',
createdAt: DateTime (2020, 1, 1),
updatedAt: DateTime (2020, 1, 1),
isDone: true,
);
test('idのテスト', () {
expect (todoItem.getId, 0);
});
- 10. Unitテストの書き方と実行方法
プロジェクトルートの
testフォルダの下に
XXX_test.dartファイルを作成
import 'package:flutter_test/flutter_test.dart' ;
import
'package:todo_app_sample_flutter/data/todo_item.dart' ;
void main() {
group('TodoItemのゲッターのテスト ', () {
final TodoItem todoItem = TodoItem(
id: 0,
title: 'title',
body: 'body',
createdAt: DateTime (2020, 1, 1),
updatedAt: DateTime (2020, 1, 1),
isDone: true,
);
test('idのテスト', () {
expect (todoItem.getId, 0);
});
main関数の中に
実際のテストを記載
test(‘テストケース名’,(){
実際のテスト処理
expect(結果,期待する値);
});
group()でテストケースを
まとめることが出来る。
- 12. ここからが本題
class MemoDetailModel extends ChangeNotifier {
final FirebaseAuth auth = FirebaseAuth.instance;
final FirebaseFirestore firestore = FirebaseFirestore.instance;
Future addMemo() async {
final memo = Memo( ~~ );
~~何かデータ追加前にチェックしたりとか~~
final collection = firestore.collection('users');
final user = auth.currentUser;
if (user != null) {
collection.doc(user.uid).collection('memos').add({
'title': memo.title,
'updatedAt': memo.updatedAt,
'happenedAt': memo.happenedAt,
});
}
notifyListeners();
}
}
よくありそうなChangeNotifierを
継承したドメインモデル
ビジネスロジックを実装しているので
単体テストを行いたいが、
右のような状態ではテスト出来ない。
どこが問題?
- 22. インターフェースの使い方
・コンストラクタで
インターフェースを受け取る
・インターフェースにはメソッドの
決まりが書いてあるので、
記載のあるメソッドは
そのまま使うことが出来る
class TodoItemDetailModel extends ChangeNotifier {
TodoItemDetailModel ({
@required TodoItemRepository todoItemRepository ,
}) : _todoItemRepository = todoItemRepository ;
final TodoItemRepository _todoItemRepository ;
Future <void> add() async {
if (todoTitle == null || todoTitle .isEmpty) {
final Error error = ArgumentError ('タイトルを入力してください。
');
throw error;
}
await _todoItemRepository .create(
~~
);
notifyListeners ();
}
https://github.com/tokku5552/TODOAppSample-Flutter/blob/v1.3/lib/presentation/todo_item_detail/todo_item_detail_model.dart
- 23. インターフェースの使い方
・コンストラクタで
インターフェースを受け取る
・インターフェースにはメソッドの
決まりが書いてあるので、
記載のあるメソッドは
そのまま使うことが出来る
class TodoItemDetailModel extends ChangeNotifier {
TodoItemDetailModel ({
@required TodoItemRepository todoItemRepository ,
}) : _todoItemRepository = todoItemRepository ;
final TodoItemRepository _todoItemRepository ;
Future <void> add() async {
if (todoTitle == null || todoTitle .isEmpty) {
final Error error = ArgumentError ('タイトルを入力してください。
');
throw error;
}
await _todoItemRepository .create(
~~
);
notifyListeners ();
}
https://github.com/tokku5552/TODOAppSample-Flutter/blob/v1.3/lib/presentation/todo_item_detail/todo_item_detail_model.dart
外部通信など(DBやFirebaseを使う処理)はリ
ポジトリーというクラスに集約して、
ビジネスロジックから切り離す
ビジネスロジックはインターフェースに
依存する。(具体的な実装に依存しない。)