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] Unitテストの基礎を理解する
Search
h.uriu
June 25, 2024
Programming
1.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
[Flutter] Unitテストの基礎を理解する
「モバイルアプリ開発における良いテストコードの考え方」の発表資料になります。
https://trident-qa.connpass.com/event/320151/
h.uriu
June 25, 2024
More Decks by h.uriu
See All by h.uriu
【生成AI時代の資料作り革命】AIっぽさを消すために、あえてAIを使う
haruki_uiru
0
74
Other Decks in Programming
See All in Programming
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.6k
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
590
スマートグラスで並列バイブコーディング
hyshu
0
260
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
400
決定論的オーケストレーションの設計と実装 / Design and Implementation of Deterministic Orchestration
nrslib
4
1.5k
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
840
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
300
AI駆動開発を妨げる技術的負債の解消アプローチ / ai-refactoring-approach
minodriven
14
7.2k
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
130
技術的負債解消で開発者の未来を開く- AIの力でコード刷新
kmd2kmd
0
120
ふつうのFeature Flag実践入門
irof
8
4.2k
Skillsは効率化、Agentsは"自分の拡張"——Builder時代のエージェント編成(CC Night 2026)
wemra
1
170
Featured
See All Featured
How To Speak Unicorn (iThemes Webinar)
marktimemedia
1
490
How STYLIGHT went responsive
nonsquared
100
6.2k
The Curious Case for Waylosing
cassininazir
1
400
Bash Introduction
62gerente
615
220k
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.1k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.5k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
23k
The Spectacular Lies of Maps
axbom
PRO
1
820
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
62
55k
Statistics for Hackers
jakevdp
799
230k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.1k
Color Theory Basics | Prateek | Gurzu
gurzu
0
370
Transcript
Unitテストの基礎を理解する 株式会社イーディーエー システム開発部 瓜生 遥輝
自己紹介 瓜生 遥輝 / Uriu Haruki 株式会社イーディーエー / Flutter Engineer
Qiitaアカウント: https://qiita.com/haru-qiita 趣味: フットサルとサッカー観戦
目次 01 Unitテストの基本 02 Widgetテストを学ぶ 03 単一責任に焦点を当てる
01 Unitテストの基本
テストコード実行手順 パッケージを追加 テストコードを格納するディレクトリを作成 テストコードの実装 テスト実行
my_project/ ├── lib/ │ ├── models/ │ │ └── user.dart
│ ├── services/ │ │ └── user_service.dart │ └── utils/ │ └── string_utils.dart └── test/ ├── models/ │ └── user_test.dart ├── services/ │ └── user_service_test.dart └── utils/ └── string_utils_test.dart
$ flutter test 00:02 +3: All tests passed! 実装が完了した後は、flutter test
をターミナルで 実行して、テスト結果を確認します。 00:02 : テストが実行された総時間 +3 : テストが成功したことを 示す値、成功であれば`+` 失敗した 場合は `-` が表示されます。 All tests passed! : すべてのテストが成功したというメッ セージです。 プロジェクト内のすべてのテストケース が正常に実行されていることを表して います。 失敗 成功
基本メソッド • testOn • timeout • skip • tags •
onPlatform • retry test() expect() その他 • reason • skip • matcher • group() • setUp() • setUpAll() • tearDown() • tearDownAll()
test() テストケースを定義するためのメソッドです test('subtract', () { // Arrange const a =
8; const b = 3; // Act final result = mathOperations.subtract(a, b); // Assert expect(result, equals(5)); }); } body description 必須のプロパティは、以下の2つになります。 • description: テストケースの説明を文字列で指定します。 この説明はテスト結果の表示やテスト実行時のログに 使用されます • body: テストの本体であるテストコードを記述します。
expect() ユニットテストでアサーションを行うためのメソッドです。必須のプロパティは、以下の 2つになります。 • actual: テストで得られた実際の結果や値を指定します。 • matcher: 期待する結果を表すマッチャーを指定します。 expect(actual,
matcher);
"matcher"とは、期待する結果を表すためのオブジェクトです。 matcher を使用することで、テストケースで得られた実際の結果と期待する 結果を比較し、アサーションを行うことができます。 なぜ matcherを使用するのか? 1. 複雑な条件をチェック 2. テストの可読性とメンテナンス性の向上
3. より詳細なエラーメッセージ matcher
matcher メソッド 機能 equals マッチャー 2つのオブジェクトが等しいことを確認します。 isTrue マッチャー 真偽値が true
であることを確認します。 isFalse マッチャー 真偽値が false であることを確認します。 isNull マッチャー 値が null であることを確認します。 contains マッチャー コレクションが指定した要素を含んでいることを確認します。 isA マッチャー オブジェクトが指定した型であることを確認します。 throwsA マッチャー 指定した例外をスローすることを確認します。 hasLength マッチャー コレクションが指定した長さであることを確認します。
// equals マッチャー test('equals sample', () { int actual =
5; int expected = 5; expect(actual, equals(expected)); }); // isTrue・isFalse マッチャー test('isTrue・isFalse sample', () { bool condition = true; expect(condition, isTrue); }); // isNull マッチャー test('isNull sample', () { String? value; expect(value, isNull); }); // contains マッチャー test('contains sample', () { List<int> numbers = [1, 2, 3, 4, 5]; int value = 3; expect(numbers, contains(value)); });
isA マッチャー オブジェクトが指定した型で あることを確認します。 // isA マッチャー test('isA sample', ()
{ expect('Hello', isA<String>()); }); throwsA マッチャー 指定した例外をスローすることを確認 します。 // throwsA マッチャー test('throwsA sample', () { expect(() => throw Exception('TestException'), throwsA(const TypeMatcher<Exception>())); }); // hasLength マッチャー test('hasLength sample', () { expect('Hello', hasLength(5)); }); // isEmpty マッチャー test('isEmpty sample', () { List<int> emptyList = []; expect(emptyList, isEmpty); });
setUp() 各テストケースの前に共通の処理を 実行するために使用されます。 tearDown() 各テストケースの後に共通の処理を 実行するために使用されます。 setUpAll() テストグループ内のすべての テストの開始前に1回だけ 実行されます。
tearDownAll() テストグループ内のすべての テストの終了後に1回だけ 実行されます。
Unitテストの書き方 Unitテストコードを書く際は、 AAAパターン (Arrange-Act-Assert) という パターンを遵守して実装します。 AAA パターンとは、テストコードを Arrange(準備)、Act(実行)、Assert(確認)の3 つのフェーズに分けて書くことを言います。
AAAパターンを採用することで、テストが 明確になり、テストコードが読みやすくなります。
具体例として、加算(足し算)・減算(引き算)の例を 考えてみましょう。 右のコードは足し算・引き算の関数を 定義した MathOperations クラスです。 class MathOperations { int
add(int a, int b) { return a + b; } int subtract(int a, int b) { return a - b; } }
import 'package:flutter_test/flutter_test.dart'; import 'package:tester/main.dart'; void main() { late MathOperations mathOperations;
setUp(() { mathOperations = MathOperations(); }); test('add', () { // Arrange const a = 5; const b = 3; // Act final result = mathOperations.add(a, b); // Assert expect(result, equals(8)); }); test('subtract', () { // Arrange const a = 8; const b = 3; // Act final result = mathOperations.subtract(a, b); // Assert expect(result, equals(5)); }); }
02 Widgetテストを学ぶ
Widgetテストとは、UIコンポーネント(ウィジェット)が期待通りに 描画されていることを検証するためのテストです。 Unitテスト 単一の関数や メソッドを対象に ビジネスロジックを 検証します。 Integrationテスト アプリ全体の動作を 検証しウィジェット間の連携
や外部サービスとの統合を 検証します。 Widgetテスト 単一または複数の ウィジェットを対象に UIのレンダリングと 動作を検証します。 Widgetテストとは
メソッド 機能 pumpWidget 指定されたウィジェットをレンダリングします pump フレームを進めてウィジェットツリーを再描画します tap 特定のウィジェットをタップします enterText 特定のテキストフィールドにテキストを入力します
drag 特定のウィジェットをドラッグします longPress 特定のウィジェットを長押しします 基本メソッド
メソッド 機能 find.text 特定のテキストを持つウィジェットを検索します find.byType 指定されたタイプ(クラス)のウィジェットを検索します find.byKey 特定のキーを持つウィジェットを検索します find.byIcon 特定のアイコンを持つウィジェットを検索します
find.byWidget 特定のウィジェットインスタンスを検索します find.byTooltip 特定のツールチップテキストを持つウィジェットを検索します Finderメソッド Finderメソッドとは、テスト環境でレンダリングされたWidgetツリーの中から特定の Widgetを見つけるためのメソッドです。
findsOneWidget 1つのウィジェットが見つかる ことを確認するために使用します。 findsNothing ウィジェットが見つからない ことを確認するために使用します。 findsWidgets 2つ以上のウィジェットが見つかること を確認するために使用します。 findsNWidgets
指定された数のウィジェットが 見つかることを確認するために 使用します。
testWidgets('Counter increments Widget test', (WidgetTester tester) async { // Arrange:
テストデータの準備 await tester.pumpWidget(MyApp()); // 初期状態の検証 expect(find.text('0'), findsOneWidget); expect(find.text('1'), findsNothing); // Act: テストの実行 await tester.tap(find.byIcon(Icons.add)); await tester.pump(); // Assert: テスト結果の検証 expect(find.text('0'), findsNothing); expect(find.text('1'), findsOneWidget); }); 1. Arrange(準備) テスト対象のウィジェットをレンダリングし、 初期状態を設定します。 2. Act(実行) tester.tapメソッドを使って、フローティングアク ションボタン(FAB)をタップし、その後 tester.pump メソッドを使ってウィジェットツリーを再描画します。 3. Assert(検証) 期待される結果が得られているかを確認します。 expectメソッドを使って、テキスト '0'が見つからず、 テキスト'1'が見つかることを確認します。
03 単一責任に焦点を当てる
単一責任原則 (Single Responsibility Principle) とは Robert C. Martinの定義 "Every software
component should have one and only one responsibility." 各クラスや関数が一つの責任を持つ状態 理解しやすい && 保守しやすいコード
なぜ単一責任原則 (SRP) が重要なのか? ・コードのモジュール化・テストの簡易化 ・バグの特定と修正が容易 ・コードの再利用性の向上 変更に強いコード 変更が必要な時に影響範囲が小さい
class UserManager { final UserRepository _userRepository; final AnalyticsService _analyticsService; UserManager(this._userRepository,
this._analyticsService); Future<void> signIn(String username, String password) async { User user = await _userRepository.signIn(username, password); _analyticsService.trackSignIn(user.id); } Future<void> signOut() async { await _userRepository.signOut(); _analyticsService.trackSignOut(); } } <問題点> ・UserManager クラスが UserRepository と AnalyticsService の両方の責任を持っている。 ・signIn と signOut のテストが難しくなる。 SRPが遵守されていないコード
class AuthService { final UserRepository _userRepository; AuthService(this._userRepository); Future<User> signIn(String username,
String password) { return _userRepository.signIn(username, password); } Future<void> signOut() { return _userRepository.signOut(); } } class AnalyticsManager { final AnalyticsService _analyticsService; AnalyticsManager(this._analyticsService); void trackSignIn(String userId) { _analyticsService.trackSignIn(userId); } void trackSignOut() { _analyticsService.trackSignOut(); } } <改善点> ・AuthService と AnalyticsManager という 2つのクラスに分割。 ・それぞれのクラスが単一の責任を持つ。 SRPが遵守されたコード
まとめ 01. Unitテストの基本 - 単一の関数やメソッドをテストする - 入力値と期待する出力値を用意する Arrange(準備)、Act(実行)、Assert(確認)の3ステップ - matcherを用いてAssert
(確認) を実施する 03. 単一責任に焦点を当てる - 1つのテストケースで1つの振る舞いのみをテストする - テストケースが複数の責任を持つと、保守性が低下する - テストの意図が明確になり、他の開発者が理解しやすくなる 02. Widgetテストを学ぶ - 他のテスト(UnitTest、IntegrationTest)と比較して、UIにフォーカスしたテスト - Widgetテストとは、個々のウィジェットのレイアウトや動作を検証するテスト - Finderとmatcherを使用してウィジェットの存在と状態を確認
ご清聴ありがとうございました 詳細情報: https://qiita.com/haru-qiita/items/7a539e6bee0a175bd94f