Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Flutterでもテスト駆動したい
Search
Takashi Makino
May 19, 2023
Programming
380
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Flutterでもテスト駆動したい
Takashi Makino
May 19, 2023
More Decks by Takashi Makino
See All by Takashi Makino
あなたの知らないスクラムの世界
makky0620
0
110
A_アルゴリズム高速化を目指して
makky0620
0
1.5k
Discord botにScrumの Discord botにScrumの手伝いをしてもらう手伝いをしてもらう
makky0620
0
430
Other Decks in Programming
See All in Programming
OSもどきOS
arkw
0
560
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
540
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
170
AI時代のUIはどこへ行く?その2!
yusukebe
21
7.1k
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.5k
作って学ぶ、 JSX (TSX) ランタイムの基本
syumai
7
1.6k
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
2
640
さぁV100、メモリをお食べ・・・
nilpe
0
140
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
310
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
120
Spring Security 実践 ─ GraphQL APIで実務に役立つ 認証・認可 を学ぶ
wagyu
0
230
Inside Stream API
skrb
1
710
Featured
See All Featured
Art, The Web, and Tiny UX
lynnandtonic
304
22k
Building AI with AI
inesmontani
PRO
1
1.1k
Heart Work Chapter 1 - Part 1
lfama
PRO
7
36k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
1
1.3k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.2k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
52
6k
The Straight Up "How To Draw Better" Workshop
denniskardys
239
140k
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
530
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
Claude Code のすすめ
schroneko
67
230k
Transcript
Flutterでもテスト駆動したい 牧野孝史@合同LT会 in 横浜 たくさんFlutterを触っている⼈には当たり前かも… もっとこうした⽅が良いがあれば教えてください
Takashi Makino makky0620 未来シェア Overview About me 未来シェア 最適化っぽいコードを書いている ただ、フロントもAPIも⾊々やる!
公⽴はこだて未来⼤学 ⺟校‧北海道函館市にある情報系の⼤学 今回のLT会を企画するきっかけに ⼭の上にあって⾃転⾞で⾏くのは⼤変 キャンプ 最近キャンプしてきて趣味にしたいと思っている 焚き⽕と飯が最⾼ Engineering Recent memory
Takashi Makino makky0620 未来シェア Overview Popular language or frameworks Python
さくっと書きたい時に使う 仕事ではちょっとしたスクリプト作成 趣味では機械学習で使⽤ Java(Spring Boot) はじめましては⼤学1年⽣ Spring Bootをはじめたのは仕事をはじめてから オブジェクト指向とStreamAPIが好き TypeScript(React) ⼤学時代のバイトで使いはじめた(当時はJS) フロントアプリを作るといえばこいつ 最近あまり書いてない Dart(Flutter) 2023年1⽉から始めた(2018年に⼀度挫折) 仕事でネイティブアプリが作りたくなった 徐々にわかってきた気がしたので今⽇発表 Engineering
テスト駆動開発 Test Driven Development: TDD
テスト駆動開発とは ユースケース検討 テストを書く きれいにする 成功するコードを書く RED GREEN REFACTOR
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい // テストコード // プロダクションコード
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい void main() { test(ʻ数値である時trueを返す’, () { expect(isNumeric(ʻ123’), true);
}); } bool isNumeric(String text) { throw UnsupportedError(ʻerror’); } // テストコード // プロダクションコード
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい void main() { test(ʻ数値である時trueを返す’, () { expect(isNumeric(ʻ123’), true);
}); } bool isNumeric(String text) { return true; } // テストコード // プロダクションコード
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい void main() { test(ʻ数値である時trueを返す’, () { expect(isNumeric(ʻ123’), true);
}); // テストを追加 test(ʻ数値でない時falseを返す’, () { expect(isNumeric(ʻhoge’), false); }); } bool isNumeric(String text) { return true; } // テストコード // プロダクションコード
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい void main() { test(ʻ数値である時trueを返す’, () { expect(isNumeric(ʻ123’), true);
}); test(ʻ数値でない時falseを返す’, () { expect(isNumeric(ʻhoge’), false); }); } bool isNumeric(String text) { bool isNumeric = false; try { int number = int.parse(text); isNumeric = true; } catch (e) { isNumeric = false; } return isNumeric; } // テストコード // プロダクションコード
具体的にどうやるの? 受け取った⽂字列が数値かどうか判定する関数を作りたい void main() { test(ʻ数値である時trueを返す’, () { expect(isNumeric(ʻ123’), true);
}); test(ʻ数値でない時falseを返す’, () { expect(isNumeric(ʻhoge’), false); }); } bool isNumeric(String text) { return int.tryParse(text) != null; } // テストコード // プロダクションコード
依存性注⼊ Dependency Injection: DI
DIとは? 必要なものを外部から⽤意するような設計パターン class UserService { final UserRepository _repository; UserService(this._userRepository); Future<User>
getUser(String id) async { return await _repository.fetchBy(id); } } void main() async { final repository = UserRepository(); final service = UserService(repository); final user = await service.getUser(ʻ123’); } class UserService { final UserRepository _repository; UserService() { _repository = UserRepository(); }; Future<User> getUser(String id) async { return await _repository.fetchBy(id); } } void main() async { final service = UserService(); final user = await service.getUser(ʻ123’); } // DI使⽤ // DI未使⽤
DIを使うとテストが書きやすくなる? 依存関係をモックしてテストができる class UserService { final UserRepository _repository; UserService(this._userRepository); Future<User>
getUser(String id) async { return await _repository.fetchBy(id); } } void main() async { final repository = UserRepository(); final service = UserService(repository); final user = await service.getUser(ʻ123’); } class UserRepositoryMock { Future<User> fetchBy(String id) async { return User(id: id); } } void main() { final mockRepository = UserRepositoryMock(); final service = UserService(mockRepository); test(ʻ指定されたidのユーザを返すこと’, () async { final actual = service.getUser(ʻ123’); expect(actual, User(id: ʻ123’)); }); } 依存関係をテストから分離 // テストコード
FlutterでDIやモックをするために Riverpod mockito Flutterの状態管理ライブラリ でありDIフレームワーク テスト中のクラスの依存関係を モックできるテストフレーム ワーク
RiverpodでDI final userViewModelProvider = StateNotifierProvider.autoDispose((ref) => UserViewModel(...)) class UserViewModel StateNotifier<...>
{ ... Future<void> load() async { final users = await repository.fetchAll(); state = UserState(users: users); } } class UserPage extends HooksConsumerWidget { const UserPage({Key? key}): super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { final viewModel = ref.read(userViewModelProvider.notifier) return Scaffolding(...); } } Providerを⽤いてDIコンテナに登録し、WidgetRef.readを⽤いて依存性注⼊ 依存性注⼊ DIコンテナ登録
mockitoでモックしてテスト class UserRepositoryMock { Future<User> getUser(String id) async { return
User(id: id); } } void main() { final mockRepository = UserRepositoryMock(); final service = UserService(mockRepository); test(ʻ指定されたidのユーザを返すこと’, () async { final actual = await service.getUser(ʻ123’); expect(actual, User(id: ʻ123’)); }); } @GenerateMocks([UserRepository]) void main() { final mockRepository = MockUserRepository(); final service = UserService(mockRepository); test(ʻ指定されたidのユーザを返すこと’, () async { when(mockRepository.getUser(ʻ123’)) .thenAnswer((_) => Future.value(User(id: ʻ123’))); final actual = await service.getUser(ʻ123’); verify(mockRepository.getUser(ʻ123’)).called(1); expect(actual, User(id: ʻ123’)); }); } // mokito未使⽤ // mokito使⽤ whenで戻り値の設定、verifyでモックインスタンスの呼び出し検証ができる
実践 わかるけどできないんだよなぁ
Todoアプリを例にテスト駆動開発してみる Todoの⼀覧表⽰ 新規Todoの作成 Todoの⼀覧表⽰ ※本当は適当に作った画⾯イメージで良い
どんな機能を実装したい? 1. 右下のボタンを押した時、新規作成画⾯が表⽰されること 2. Todoの項⽬があった時、タイトルが表⽰されていること 3. TBD... Todoアプリを例にテスト駆動開発してみる
右下のボタンどうしようかなぁ → FloatingActionButtonで実装しよう 遷移後の画⾯の名前どうしようかな → TaskEditPageにしよう テスト駆動開発を実践 void main() {
testWidgets(ʻテストケース名', (tester) async { // 対象画⾯の描画 await tester.pumpWidget(ProviderScope( child: const MaterialApp(home: HomePage()))) // ボタンを押す await tester.tap(find.byType(FloatingActionButton)); await tester.pumpAndSettle(); // 新規作成画⾯への遷移を検証 expect(find.byType(TaskEditPage), findsOneWidget); }); } 「右下のボタンを押した時、新規作成画⾯が表⽰されること」をテストする
Todoの情報はどこから持ってくる? → Repository層から持ってくる どこに表⽰させようかな → TaskItemウィジェットにを作ってそ の中に表⽰しよう テスト駆動開発を実践 void main()
{ testWidgets(ʻテストケース名', (tester) async { // Todo項⽬のモック var task = Task( when(taskRepository.fetchAll()) .thenAnswer((_) => Future.value([task])); // 対象画⾯の描画 await tester.pumpWidget(ProviderScope( child: const MaterialApp(home: HomePage()))) await tester.pumpAndSettle(); // タイトルが表⽰されていることの検証 var titleFinder = find.descendant( of: find.byType(TaskItem), matching: find.text(ʻタイトル’) ); expect(find.byType(TaskEditPage), findsOneWidget); }); } 「Todoの項⽬があった時、タイトルが表⽰されていること」をテストする 最⼩限の設計+品質の作り込み
実際に書いてみちゃうかぁ 時間があったらですが...
今⽇のキーワード • テスト駆動開発:テストから書くことで品質の⾼いコードを作る開発技法 • DI(依存性注⼊):テストしやすくするための設計パターン Flutterでテスト駆動開発するために • riverpod:DI機能を提供してくれるパッケージ • mockito:モックオブジェクトが作れるようになるパッケージ
所感(フロントのテストを書き慣れてない) • 親‧⼦ウィジェットでのテストケースの棲み分けがわからないなぁ 今⽇のコード:https://github.com/makky0620/flutter-demo まとめ:テストから書くのはいいぞぉ