Riverpodでテストを書こう
FlutterGakkai 2022/1/29
目次
● Flutterでのテストことはじめ
● サンプルプロジェクトの解説
● Unit Test
● Widget Test
● まとめ
2
自己紹介
とっく(@tokkuu)
● 都内AdTech企業のWebエンジニア
● php/python/TypeScript
● AWS周り 3-tierからサーバレスまで
● 元々はSIerのインフラエンジニア
● Flutter歴は1年半くらい
3
はじめに
● 説明すること
○ Riverpodでのユニットテストの書き方
○ RiverpodでのWidgetテストの書き方
● 説明しないこと
○ DIを用いたProviderでのテストの書き方
○ Integration Testについて
○ CIへの組み込みやDDDについて
● Providerでのテストについて
○ Flutterで単体テストを行う方法とGitHub Actionsを使った自動化
● DDDの説明(同じサンプルを使用)
○ hooks_riverpod + state_notifier + freezedでのドメイン駆動設計
4
Flutterでのテストことはじめ
Flutterにおけるテストの種類
Flutterには3種類のテストがある
公式ページ:https://flutter.dev/docs/cookbook/testing
・Unit Test   
・Widget Test 
・Integration Test
いわゆる単体テスト。関数、メソッド、クラスの検証を行う
Widgetが正しく生成されるかのテスト。
結合テスト。シナリオを書いてエミュレータ上で自動操作によるテス
トが行える。
6
Unit Testの準備
・パッケージの導入
 pubspec.yamlにflutter_testが追加されていること
7
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);
});
8
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()でテストケースを
まとめることが出来る。
9
テスト実行
1. テストファイルを右クリック
2. Run ‘test in <テストファイル>’
デバッグの画面が開いて
結果が表示される
10
サンプルプロジェクトの解説
サンプルプロジェクトの説明
● DDDぽく書いている
● application層の
todo_app_service.dartがdomain層の
classを使う形
● ビジネスロジックのテストという文脈で
は、todo_app_service.dartを
テストすれば十分
● このプロジェクト自体の解説は
Qiitaの記事参照
● 全体のコードはこちら
12
サンプルプロジェクトの説明 - TodoAppService
● todo_app_serviceは
todoListRepositoryに依存して
いる
● todoListRepositoryはFirebase
との通信を担う
● DDDじゃない場合、MVVM +
Repositoryのパターンなどで
は、View Modelでテストすれば
良い
13
テストの準備 - TodoListRepositoryのモックを作る
● TodoListRepositoryを実装した
TodoListRepositoryMemを作
る
● TodoListRepositoryMemでは
Firebaseとの通信を行わず、テ
スト用に一時的にデータを貯め
れるように実装しておく
● コード
14
テストの準備 - ProviderContainerでoverrideする
● Riverpodでは
ProviderContainerを使ってモッ
ク用のクラスを宣言することで、
Providerを上書きできる。
● 右のように
TodoListRepositoryMemを
宣言しておくことで
このコンテナから呼び出したリ
ポジトリやサービスでは
上書きされたモック用クラスが
呼び出される様になる。
15
Unit Test
Unit Test - 宣言
● Widgetの生成が絡まない
ロジックをテストする
● setUpやtearDownが使える
ので、先程のコンテナの宣言や
データのセットが行える。
● Null Safetyなバージョンの
Flutterであれば、コンテナや
モックはlateで宣言しておくこと
でnon-nullableに扱える
17
Unit Test - 実行
1. 空のTodoItemを_repositoryに
セットし、自動生成され
割り当てられたnextIdを取得
2. テスト対象のクラスを
インスタンス化
3. テスト対象のメソッドを
実行し結果を
expectメソッドで比較
2.
3.
1.
18
Widget Test
WidgetTest - 宣言
● モック用クラスは引き続き
containerから取得
● Widgetを生成するために、
tester.pumpWidgetの中で
ProviderScopeを宣言し、モック
にoverride
● childの中でテスト対象Widget
の描画に必要なWidget、
MaterialAppなどを宣言した上
で対象のWidgetを宣言
20
WidgetTest - 実行
● find.textで描画されたWidgetの
中のテキストを探す。
findsOneWidgetは「それが1つ
あること」という意味になる。
● tester.tapなどで、特定のIcon
などを探してボタンをタップさせ
ることもできる。
21
まとめ
● Riverpodで記述するときは、Repositoryをあとからoverrideできるため、わざわざ
抽象クラスを作っておいて、DIできるようにドメインクラスを実装する必要がない
● Unit Testのほうが比較的簡単に、最低限のロジックのテストが書きやすい
● CI組んでおけば、快適にTDDできそう
● Widget Testまで書いて、Codecovなどを使ってカバレッジを可視化して、CI組んで
Slack通知まで設定しておくと快適に、より安全に開発が進められると思いました。
(関連記事)
ご清聴ありがとうございました

Riverpodでテストを書こう