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
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
370
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
130
1年で人数1.5倍、PR数5.5倍増。 品質とアウトカムはどうなったか、 何が効いたか
ike002jp
0
140
アルゴリズムは何を圧縮しているのか ─ Haskell から育った「圧縮代数」というメンタルモデル
naoya
16
3.6k
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
180
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
390
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
1
350
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
3k
act1-costs.pdf
sumedhbala
0
240
地域 SRE コミュニティ最前線 - ホンマでっかSRE勉強会
tk3fftk
0
270
Generative UI & AI-Assistants for Your Angular Solutions
manfredsteyer
PRO
1
210
ITヒヤリハットを整理してみた ~ライフサイクルと原因から考える再発防止策~
koukimiura
1
110
Featured
See All Featured
Prompt Engineering for Job Search
mfonobong
0
380
The Cost Of JavaScript in 2023
addyosmani
55
10k
A Tale of Four Properties
chriscoyier
163
24k
Bash Introduction
62gerente
615
220k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
28
3.6k
Optimizing for Happiness
mojombo
378
71k
KATA
mclloyd
PRO
35
15k
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
330
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
360
30k
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 まとめ:テストから書くのはいいぞぉ