SlideShare a Scribd company logo
1 of 32
Download to read offline
画面状態を抽象化してテスタブル設計ライフ
を送ろう
kikuchy
Who?
@kikuchy
菊池紘
株式会社Diverse (ミクシィグループ)
テストの大切さは知っているけれど腰が重い
突然ですが
こんなコードに出くわしたことはないですか
var page = 1
var isLoading = false
var hasBeenReachedPageEnd = false
var isInitialLoading = true
fun showData(data: Data) {
page++
hasBeenReachedPageEnd = checkPageEnd(data)
adapter.setDataAndNotifyChanged(data)
}
if (!isLoading && !hasBeenReachedPageEnd) {
repository
.getData()
.subscribe(object: Observer {
override fun onNext(data: Data) { showData(data) }
override fun onStart() { isLoading = true;
showProggress(isInitialLoading) }
override fun onComplete() { isLoading = false;
hideProgress(isInitialLoading);
isIlitialLoading = false }
})
}
複数フラグで状態管理= 終わりの始まり
booleanのフラグが3つあるとして、とり得る状態は本当に8通りな
のか?
実際は5通りくらいだったりする
あり得ない状態を考慮できてしまうのは混乱の元
いつ/どこでフラグいじる?問題
どこで変更するの?
どこで参照するの?
いま変更して大丈夫なの?
Activity / Fragmentに生えたメンバ変数は実質グローバル変数
こんな地獄を防ぐには
状態を列挙
状態の操作をできる場所を制限
状態が正しく切り替わっているか確認
状態をオブジェクトで管理して
挙動のテストを書く!
ちゃんと考えよう
前提
APIからとってきた情報を表示するアプリ
‑> 通信状況≒ 画面の状態
暗黙的に期待されている機能は洗い出せているとする
読み込み中の表示
再読込
ページング
失敗時の表示
作例
DroidKaigi 2017のStargazersを表示する
https://github.com/kikuchy/ScreenStateIsModelSample
いきなりページングする画面のことを考えるのはしんどいので
簡単な状態のものからステップアップして考えていきます
状態
ページングなし、再取得なし、読んだ情報を表示するだけの画面
読み込みしていない[初期状態]
読み込み中
読み込み成功(with 読み込めたデータ)
読み込み失敗(with エラー内容)
sealed class State {
// 読み込みしていない
object NeverFetched : State()
// 読み込み中
object Fetching : State()
// 読み込み成功
data class Success(val data: Data) : State()
// 読み込み失敗
data class Failure(val error: Throwable) : State()
}
状態遷移
遷移を考える
どの状態からどの状態へ遷移可能なのか
どんな操作によって遷移が起こるのか
操作を受け付けられない状態であったら無視することにす
る
図
+--------------+
| NeverFetched |
+--------------+
|
| fetch
v
+----------+
| Fetching |
+----------+
|
| (読み込みが終わったら自動で遷移)
+---------------------
| |
v v
+---------------+ +----------------+
| Success(data) | | Failure(Error) |
+---------------+ +----------------+
状態の管理場所を作る
状態遷移モデルなので、 モデルと呼ぶ
ステートマシンと呼んだ方がしっくり来る人もいる
状態の変更はモデル外部で受け取ってもらう必要がある
今回はRxJavaを使用
自前でObserverパターンを実現できればそれでも良い
モデル内部では現在の状態を保持している必要がある
通知も同時にできるBehaviorSubjectが適任
class Model(private val repository: Repository) {
// 状態を保持するメンバ代わり。通知役も兼ねる。
private val stateHolder =
BehaviorSubject
.createDefault<State>(State.NeverFetched)
// 外にはObservableとして公開
val stateObservable: Observable<State>
get() = stateHolder
private fun fetchRepo() {
// 読み込みが始まるときに、読み込み中状態にする。
stateHolder.onNext(State.Fetching)
repository.getData().subscribe({ data ->
// 読み込み成功
stateHolder.onNext(State.Success(data))
}, { error ->
// 読み込み失敗
stateHolder.onNext(State.Failure(error))
})
}
fun fetch() {
// NeverFetched状態でなければ要求を無視する
when (stateHolder.value) {
is State.NeverFetched -> fetchRepo()
else -> return
}
}
}
図のとおりになっているはず
+--------------+
| NeverFetched |
+--------------+
|
| fetch
v
+----------+
| Fetching |
+----------+
|
| (読み込みが終わったら自動で遷移)
+---------------------
| |
v v
+---------------+ +----------------+
| Success(data) | | Failure(Error) |
+---------------+ +----------------+
ステップアップ
再読込もページングもする画面の状態
読み込みしていない[初期状態]
読み込み中 (with ページ番号、今までに読み込んだアイテム)
読み込み成功(with ページ番号、アイテム+ 新しいアイテム)
読み込み失敗(with ページ番号、エラー、アイテム)
ページ終端(with アイテム+ 新しいアイテム)
読み込みに失敗してページ終端に到達することはない
操作
次のページを読み込む
読み込みに成功したらアイテムを増やし、ページ番号を増やす
再読込
抱えてるアイテムを破棄して、ページ番号を1に戻す
感覚はわかったと思うのでコードは省略
テスト
ちゃんと遷移できているかをテスト
画面にどう反映するかはモデルの関心事ではない
テストすることはそこまで多くないから気が重くならない
※もちろん、モデルの複雑さによります
画面とつなぐ
使ってみたかったのでLiveDataでイベントを流す
RxLifrcycleを使えればそれでもよい
LiveDataのObserverが表示を制御するようにする
RecyclerViewのAdapter君
エラー用のSnackBar表示する君
読み込み中インジケーター表示する君
など
個別なので管理がすごく楽
よかったこと
基本的なところは楽にテスト書ける
画面を作る前に動作確認できた
テストするうちに抜けていた状態に気づくこともある
どこに新しい機能を置けば良いのか悩まなくていい
少なくとも、状態と表示、どちらのことなのかは悩まないはず
1ファイルあたりのコード少なくて感動する
モデルがライフサイクルを超えて生き残ってくれれば、Activity /
Fragmentが表示された瞬間に以前の状態が復元される
各種アーキテクチャパターンに発展可能
大変だったこと
クラスは多い
べた書きに比べれば初期開発の時間はかかる
慣れないと大変
暗黙的に期待されている状態を洗い出すのが一番大変
githubのREST API辛くないすか
ヘッダのパース面倒とか
rate limit引っかかって死ぬとか
Activity / Fragmentが重くてお困りの方
まずは画面の状態を別クラスで
管理するところから始めてみては?
ちなみに
このアーキテクチャは弊社サービスYYCのiOS版(開発中)から
ヒントを得て作りました
Special thanks iOSチームのみなさま
https://github.com/kikuchy/ScreenStateIsModelSample
画面状態を抽象化してテスタブル設計ライフを送ろう

More Related Content

What's hot

知らないと損するアプリ開発におけるStateMachineの活用法(full版)
知らないと損するアプリ開発におけるStateMachineの活用法(full版)知らないと損するアプリ開発におけるStateMachineの活用法(full版)
知らないと損するアプリ開発におけるStateMachineの活用法(full版)
Ken Morishita
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public
bonjin6770 Kurosawa
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safe
Kumazaki Hiroki
 

What's hot (20)

GoF デザインパターン 2009
GoF デザインパターン 2009GoF デザインパターン 2009
GoF デザインパターン 2009
 
純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門純粋関数型アルゴリズム入門
純粋関数型アルゴリズム入門
 
Lispmeetup #56 Common lispによるwebスクレイピング技法
Lispmeetup #56 Common lispによるwebスクレイピング技法Lispmeetup #56 Common lispによるwebスクレイピング技法
Lispmeetup #56 Common lispによるwebスクレイピング技法
 
知らないと損するアプリ開発におけるStateMachineの活用法(full版)
知らないと損するアプリ開発におけるStateMachineの活用法(full版)知らないと損するアプリ開発におけるStateMachineの活用法(full版)
知らないと損するアプリ開発におけるStateMachineの活用法(full版)
 
静的解析を使った開発ツールの開発
静的解析を使った開発ツールの開発静的解析を使った開発ツールの開発
静的解析を使った開発ツールの開発
 
「日本語LaTeX」が多すぎる件について
「日本語LaTeX」が多すぎる件について「日本語LaTeX」が多すぎる件について
「日本語LaTeX」が多すぎる件について
 
凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public凡人の凡人による凡人のためのデザインパターン第一幕 Public
凡人の凡人による凡人のためのデザインパターン第一幕 Public
 
Kotlinアンチパターン
KotlinアンチパターンKotlinアンチパターン
Kotlinアンチパターン
 
Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話Node-REDをIoTビジネスに適用するために苦労した3つの話
Node-REDをIoTビジネスに適用するために苦労した3つの話
 
Common Lisp ユーザへのScheme紹介
Common Lisp ユーザへのScheme紹介Common Lisp ユーザへのScheme紹介
Common Lisp ユーザへのScheme紹介
 
ラズベリー・パイでプログラミングと電子工作を体験してみよう
ラズベリー・パイでプログラミングと電子工作を体験してみようラズベリー・パイでプログラミングと電子工作を体験してみよう
ラズベリー・パイでプログラミングと電子工作を体験してみよう
 
「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回
「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回
「UXデザインとは」からはじめる「本流」のUXデザインはじめの一歩 | UXデザイン基礎セミナー 第1回
 
Pythonではじめるロケーションデータ解析
Pythonではじめるロケーションデータ解析Pythonではじめるロケーションデータ解析
Pythonではじめるロケーションデータ解析
 
プログラムを高速化する話
プログラムを高速化する話プログラムを高速化する話
プログラムを高速化する話
 
Marp Tutorial
Marp TutorialMarp Tutorial
Marp Tutorial
 
マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!マイクロにしすぎた結果がこれだよ!
マイクロにしすぎた結果がこれだよ!
 
12 分くらいで知るLuaVM
12 分くらいで知るLuaVM12 分くらいで知るLuaVM
12 分くらいで知るLuaVM
 
冬のLock free祭り safe
冬のLock free祭り safe冬のLock free祭り safe
冬のLock free祭り safe
 
Map
MapMap
Map
 
MySQLアンチパターン
MySQLアンチパターンMySQLアンチパターン
MySQLアンチパターン
 

More from Hiroshi Kikuchi

More from Hiroshi Kikuchi (16)

Android Developer Toolsのバグを見つけて直してもらった話
Android Developer Toolsのバグを見つけて直してもらった話Android Developer Toolsのバグを見つけて直してもらった話
Android Developer Toolsのバグを見つけて直してもらった話
 
今更聞けない? Androidのテストのいろは
今更聞けない? Androidのテストのいろは今更聞けない? Androidのテストのいろは
今更聞けない? Androidのテストのいろは
 
新規Androidアプリ開発において何より大切なこと
新規Androidアプリ開発において何より大切なこと新規Androidアプリ開発において何より大切なこと
新規Androidアプリ開発において何より大切なこと
 
モバイルアプリ開発をグッと楽にするKotlinの便利なところ3選
モバイルアプリ開発をグッと楽にするKotlinの便利なところ3選モバイルアプリ開発をグッと楽にするKotlinの便利なところ3選
モバイルアプリ開発をグッと楽にするKotlinの便利なところ3選
 
JUnit5とAndroidのテスト
JUnit5とAndroidのテストJUnit5とAndroidのテスト
JUnit5とAndroidのテスト
 
KotlinJSって正直どうなん
KotlinJSって正直どうなんKotlinJSって正直どうなん
KotlinJSって正直どうなん
 
テストコードをアプリケーションコードと同じ階層に置きたい
テストコードをアプリケーションコードと同じ階層に置きたいテストコードをアプリケーションコードと同じ階層に置きたい
テストコードをアプリケーションコードと同じ階層に置きたい
 
Android thingsやってみた
Android thingsやってみたAndroid thingsやってみた
Android thingsやってみた
 
どうしてコードはレガシーになるのか
どうしてコードはレガシーになるのかどうしてコードはレガシーになるのか
どうしてコードはレガシーになるのか
 
Androidの世界を離れて異文化留学してみた
Androidの世界を離れて異文化留学してみたAndroidの世界を離れて異文化留学してみた
Androidの世界を離れて異文化留学してみた
 
なるべくコードを書かないAndroid開発
なるべくコードを書かないAndroid開発なるべくコードを書かないAndroid開発
なるべくコードを書かないAndroid開発
 
開発チームにKotlinを導入した話
開発チームにKotlinを導入した話開発チームにKotlinを導入した話
開発チームにKotlinを導入した話
 
3分で作る Kotlin Friendly な API
3分で作る Kotlin Friendly な API3分で作る Kotlin Friendly な API
3分で作る Kotlin Friendly な API
 
Designing Auto Generated Codes
Designing Auto Generated CodesDesigning Auto Generated Codes
Designing Auto Generated Codes
 
C#でiPhone & Androidアプリ!
C#でiPhone & Androidアプリ!C#でiPhone & Androidアプリ!
C#でiPhone & Androidアプリ!
 
Ideatter : Tech-on2011決勝でのプレゼン資料
Ideatter : Tech-on2011決勝でのプレゼン資料Ideatter : Tech-on2011決勝でのプレゼン資料
Ideatter : Tech-on2011決勝でのプレゼン資料
 

Recently uploaded

Recently uploaded (11)

Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その22024/04/26の勉強会で発表されたものです。
 
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
論文紹介:Video-GroundingDINO: Towards Open-Vocabulary Spatio-Temporal Video Groun...
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版:キンドリルジャパン社内勉強会:2024年4月発表)
 
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
Observabilityは従来型の監視と何が違うのか(キンドリルジャパン社内勉強会:2022年10月27日発表)
 
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアルLoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
LoRaWAN スマート距離検出デバイスDS20L日本語マニュアル
 
新人研修 後半 2024/04/26の勉強会で発表されたものです。
新人研修 後半        2024/04/26の勉強会で発表されたものです。新人研修 後半        2024/04/26の勉強会で発表されたものです。
新人研修 後半 2024/04/26の勉強会で発表されたものです。
 
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
Amazon SES を勉強してみる その32024/04/26の勉強会で発表されたものです。
 
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
NewSQLの可用性構成パターン(OCHaCafe Season 8 #4 発表資料)
 
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
論文紹介:Selective Structured State-Spaces for Long-Form Video Understanding
 
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
LoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイスLoRaWANスマート距離検出センサー  DS20L  カタログ  LiDARデバイス
LoRaWANスマート距離検出センサー DS20L カタログ LiDARデバイス
 
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
論文紹介: The Surprising Effectiveness of PPO in Cooperative Multi-Agent Games
 

画面状態を抽象化してテスタブル設計ライフを送ろう