Upload
takao-sumitomo
View
16.909
Download
4
Embed Size (px)
Citation preview
DroidKaigi 2015/04/25 @cattaka_net
自己紹介● 住友 孝郎(Takao Sumitomo)● Androidアプリ開発者● 開発経歴
● Androidアプリ● iOSアプリ(ちょっとだけ)● 業務系Webアプリケーション● 業務系Windowsアプリ
● その他● 電子工作● OpenCV
● ウォンテッドリー株式会社所属
2014年12月〜
DroidKaigi 2015/04/25 @cattaka_net
Androidアプリ開発の経歴● HT-03Aの頃(2009年くらい)からAndroidアプリ
作ってます。● 当時は受託と派遣を主にやってました。● 手に負えなくなったアプリの改修をよくやってました
DroidKaigi 2015/04/25 @cattaka_net
ボトルネックの潜む場所
● 開発で行うこと● プログラムを書く
● ソースコード管理
● テスト
● テスト版アプリの配布
● これらをどこまで自動化するか?
DroidKaigi 2015/04/25 @cattaka_net
ボトルネックの対策● 開発で行うこと
● プログラムを書く– 諦めて書く。良いライブラリを使う。良いIDEを使う。
● ソースコード管理– GitやSVNを使う。Git FlowやGitHub Flowを使う。
● テスト– JUnitを使う。– JenkinsやTravisCIやCircleCIで自動化する。
● テスト版アプリの配布– DeployGate等を使う。
ここが一番ネックになる
DroidKaigi 2015/04/25 @cattaka_net
どうしてたか?
● エクセルのテストシート(山盛り)● エビデンス(という名のスクリーンショット)を取る● 全機種で実機テスト● 仕様変更があったとき
→全部再テストしてくださいね^^
ツライ!(゚Д゚)ツライ!(゚Д゚)
DroidKaigi 2015/04/25 @cattaka_net
よくある問題● 小さなリファクタリング
● 意識してないところで壊れる、、orz● リファクタリングしないようにする
● ハウルの動く城みたいになる● 書きたいように書けなくなるので更にストレスが貯まる
ツライ!(゚Д゚)
DroidKaigi 2015/04/25 @cattaka_net
テストを書き始める
● 最初はロジックのテストから書く● 要件一覧表の1項目1テストを書くこともやった
● 実は個人的に一番効果があった(効率悪いけど)● 「壊す恐怖」から解放された
● 以降、設計レベルからテストを書くようにした
DroidKaigi 2015/04/25 @cattaka_net
普通のテスト要求分析
基本設計
機能設計
詳細設計
コーディング
受け入れテスト
システムテスト
結合テスト
単体テスト
通常はテストも順番に作る。後から作るのは大変。
DroidKaigi 2015/04/25 @cattaka_net
アプローチ
● 外部依存する箇所にDI(オレオレ可)を入れる● 通信処理● データベース● プリファレンス
● グローバル変数を取る(static変数)● シングルトンという名のグローバル変数も取る● 投げっぱなしのスレッドを止める● 最小限のリファクタリング● ひたすらテストを書く
DroidKaigi 2015/04/25 @cattaka_net
具体的な修正3
● テスト時の構成
アプリケーション
通信処理のモックデータベースのモック
SharedPreferencesのモック
DroidKaigi 2015/04/25 @cattaka_net
具体的な修正3
● テスト時の構成
アプリケーション
通信処理のモックデータベースのモック
SharedPreferencesのモック
要はこれらをモックに差し替えられればテストが書ける
DroidKaigi 2015/04/25 @cattaka_net
SharedPreferencesの差し替え● Context#getSharedPreferencesを付ける● 呼ぶときの引数にPrefix等を付ける● 直接↑を呼ぶのではなく、Factoryクラスを作る
DroidKaigi 2015/04/25 @cattaka_net
SharedPreferencesの差し替えプロダクションコードpublic class SharedPreferencesFactory { static SharedPreferencesFactory INSTANCE = new SharedPreferencesFactory();
public static SharedPreferencesFactory getInstance() { return INSTANCE; }
public SharedPreferences newInstance(Context context, String name) { return context.getSharedPreferences(name, Context.MODE_PRIVATE); }}
テスト用のダミーpublic class DummySharedPreferencesFactory extends SharedPreferencesFactory { public SharedPreferences newInstance(Context context, String name) { SharedPreferences pref = context.getSharedPreferences( "test_" + name, Context.MODE_PRIVATE); pref.clear(); return pref; }}
テストのときはここをダミーに差し替える
DroidKaigi 2015/04/25 @cattaka_net
SQLiteOpenHelperの差し替え● RenamingDelegatingContextを使えば
一時的に別のDBファイルにできる● SQLiteOpenHelperにname=nullを渡すと
オンメモリのデータベースが作れる
どっちでもテストは書ける
DroidKaigi 2015/04/25 @cattaka_net
SqlteOpenHelperの差し替えプロダクションコードpublic class OpenHelperFactory { static OpenHelperFactory INSTANCE = new OpenHelperFactory();
public static OpenHelperFactory getInstance() { return INSTANCE; }
@Override public OpenHelper createOpenHelper(Context context) { return new OpenHelper(context); }}
テスト用のダミーpublic class DummyOpenHelperFactory extends OpenHelperFactory { public OpenHelper createOpenHelper(Context context) { Context c = new RenamingDelegatingContext(context, "test_"); return new OpenHelper(c); }}
テストのときはここをダミーに差し替える
DroidKaigi 2015/04/25 @cattaka_net
通信処理の差し替え● 予め通信処理は1つにまとめておく● オレオレDIで通信処理を差し替える● 偽の通信データを返すようにする● 偽の通信データは
androidTest下のassetsに入れる
DroidKaigi 2015/04/25 @cattaka_net
ひたすらテストを書く
● ブラックボックステスト● ウォークスルー
● コンバージョンに繋がるところは重点的に書く● ログイン周り● 応募● ユーザーのプロフィール入力
● テストの粒度はマチマチ
DroidKaigi 2015/04/25 @cattaka_net
たとえばリスト画面
● この画面に関連する部品● Activity● Adapter● ListView● Database
● どのテストを書く?
Activity
ListView
Database
Adapter
DroidKaigi 2015/04/25 @cattaka_net
Adapterのテスト● Adapter単独で考える● Contextとダミーのデータを与える● getViewで生成されたViewを確認する● InstrumentationTestCaseが使える
Activity
ListView
Database
Adapter
Context
Adapter
DummyData
生成されたView
getViewメソッド
これらが対応しているかのテストを書く
テストのときは切り離して考える
DroidKaigi 2015/04/25 @cattaka_net
Adapterのテストpublic void testGetView() { List<CheckListItem> dummys = new ArrayList<>(); { // ダミーデータを作る dummys.add(new CheckListItem(1L, 1L, 1L, "Label1")); dummys.add(new CheckListItem(2L, 2L, 2L, "Label2")); }
Context context = getInstrumentation().getTargetContext(); MyAdapter sup = new MyAdapter(context, dummys);
{ // 1つめのViewの表示内容を確認する View view = sup.getView(0, null, null); assertThat(view, is(Matchers.instanceOf(CheckedTextView.class))); assertThat(((CheckedTextView)view).getText().toString(), is("Label1")); } { // 2つめのViewの表示内容を確認する View view = sup.getView(1, null, null); assertThat(view, is(Matchers.instanceOf(CheckedTextView.class))); assertThat(((CheckedTextView)view).getText().toString(), is("Label2")); }}
DroidKaigi 2015/04/25 @cattaka_net
Databaseのテスト● CRUD系はテストを書く● RenamingDelegateContextが便利● InstrumentationTestCaseが使える
Activity
ListView
Database
Adapter
テストのときは切り離して考える
Database
DummyData1
DummyData2
insert
select
これらが対応しているかのテストを書く
DroidKaigi 2015/04/25 @cattaka_net
Databaseのテスト@Overrideprotected void setUp() throws Exception { super.setUp(); Context context = new RenamingDelegatingContext( getInstrumentation().getTargetContext(), "test_"); mOpenHelper = new OpenHelper(context);}
public void testInsertSelect() { CheckListEntry orig = new CheckListEntry(); orig.setTitle("hoge");
{ // INSERTする mOpenHelper.registerEntry(orig); } CheckListEntry dest; { // SELECTする Long id = orig.getId(); dest = mOpenHelper.findEntry(id, false); } { // 確認する assertThat(dest.getTitle(), is("hoge")); }}
DroidKaigi 2015/04/25 @cattaka_net
Activity● 外部依存やストレージのみダミーに置き換える● ActivityInstrumentationTestCase2が使える● それぞれの部品の疎通確認程度に留める
Activity
ListView
Database
AdapterActivity
ListView
DummyDatabase
Adapter
Databaseのみダミーに置き換えて考える
これらが対応しているかのテストを書く
DroidKaigi 2015/04/25 @cattaka_net
ツールやライブラリ
● 基本的に一般的なものを使用● JUnit4● ./gradlew connectedAndroidTest● ./gradlew createCoverageReport
● Espresso (android-test-kit)● UI周りのテストが簡潔に書ける
● Mockito● モックが簡単に作れる
● Crashlytics
./gradlew create(Debug/Release)CoverageReport
DroidKaigi 2015/04/25 @cattaka_net
ボトルネックの対策● 開発で行うこと
● プログラムを書く– 諦めて書く
● ソースコード管理– GitやSVNを使う。GitFlowやGitHubFlowを使う。
● テスト– JUnitを使う– JenkinsやTravisCIやCircleCIを使う
● テスト版アプリの配布– DeployGateやFabricを使う
ここが解決すれば後の自動化もできる