testterを叩け!
DESCRIPTION
ATEC Mtg#7TRANSCRIPT
![Page 1: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/1.jpg)
Testterを叩け!(ユニットテスト的な意味で)
ユニットテストの事例
2011.05.25 ATEC Mtg#7
2011年5月26日木曜日
![Page 2: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/2.jpg)
自己紹介
• Twitter id:@nowsprinting
• 名前:長谷川 孝二
• 職種:ワンマン社長(ワンマンバス的な意味で)
• 仕事:Java, Swing, Do-Ja, iOS(SIer/請負)
2011年5月26日木曜日
![Page 3: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/3.jpg)
Agenda
• 今日お話する範囲の定義(再確認)• JUnitの基礎的なこと
• Android Testing Framework の使用例
• Android Mock の使用例
2011年5月26日木曜日
![Page 4: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/4.jpg)
再確認
2011年5月26日木曜日
![Page 5: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/5.jpg)
©snskさん at ATEC Mtg#12011年5月26日木曜日
![Page 6: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/6.jpg)
©snskさん at ATEC Mtg#1
今日はここだけ
あえて除外(切り分けたい)
2011年5月26日木曜日
![Page 7: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/7.jpg)
具体的には?
2011年5月26日木曜日
![Page 8: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/8.jpg)
受け入れシステム
統合
ユニット
Activity遷移
ユースケース
Http通信
非機能要件
テストすべき内容(観点)の分布
ビジネスロジックLib正常系(Mock)
Lib異常系(Mock)
2011年5月26日木曜日
![Page 9: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/9.jpg)
受け入れシステム
統合
ユニット
Activity遷移
ユースケース
Http通信
非機能要件
テストすべき内容→テスト方法
Robolectric AndroidTest
MonkeyRunner
ビジネスロジックLib正常系(Mock)
Lib異常系(Mock)
God Hand
2011年5月26日木曜日
![Page 10: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/10.jpg)
受け入れシステム
統合
ユニット
Activity遷移
ユースケース
Http通信
非機能要件
テストすべき内容→テスト方法
Robolectric AndroidTest
MonkeyRunner
ビジネスロジックLib正常系(Mock)
Lib異常系(Mock)
God Hand
Android特有の観点・低メモリでActivity開放・移動機を縦→横に・着信→suspend→resume
・電波不安定(そろそろ検討着手を希望!)
2011年5月26日木曜日
![Page 11: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/11.jpg)
受け入れシステム
統合
ユニット
Activity遷移
ユースケース
Http通信
非機能要件
テストすべき内容→テスト方法
Robolectric AndroidTest
MonkeyRunner
ビジネスロジックLib正常系(Mock)
Lib異常系(Mock)
God Hand
今日はここだけ
InstrumentalTestCase系
2011年5月26日木曜日
![Page 12: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/12.jpg)
JUnitの基礎的なこと
• Android Testing FrameworkはJUnit3ベース
• TestCaseからプロダクトコードを呼ぶ
• TestCase内で、Assertionによって正否を判定する。
2011年5月26日木曜日
![Page 13: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/13.jpg)
package jp.group.android.atec.testter.logic;
public class TwitterLogicTest extends AndroidTestCase {}
単純なTestCaseの例
プロダクトコード
テストコード 同じpackage名(が便利)
Class名の後ろに”Test”JUnitのTestCaseを継承
package jp.group.android.atec.testter.logic;
public class TwitterLogic {}
2011年5月26日木曜日
![Page 14: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/14.jpg)
public void testTwitterLogicAuthorization() { Authorization auth4test = ...
TwitterLogic target = new TwitterLogic(auth4test);
assertNotNull("Twitterインスタンスが生成されていること", target.twitter);}
単純なテストメソッドの例“test” + 対象メソッド名 + ケース名
対象メソッド呼び出し
Assertion(用途にあったものを)
第一引数に評価内容を書く(推奨)
2011年5月26日木曜日
![Page 15: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/15.jpg)
実行結果(失敗)失敗!
赤い!
失敗!
assert()の第一引数
2011年5月26日木曜日
![Page 16: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/16.jpg)
実行結果(成功)失敗ゼロ!
グリーン!
2011年5月26日木曜日
![Page 17: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/17.jpg)
Android Testing Frameworkの使用例
2011年5月26日木曜日
![Page 18: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/18.jpg)
Test Project を作る
2011年5月26日木曜日
![Page 19: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/19.jpg)
eclipseの場合、package explorerで製品プロジェクトを選択して右クリックして、
New Test Project... を選択。
2011年5月26日木曜日
![Page 20: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/20.jpg)
Dialog (1/2)TestProject名
製品Project名(テスト対象)
下にスクロール2011年5月26日木曜日
![Page 21: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/21.jpg)
Dialog (2/2)
テストアプリケーションのpackage名
• 製品とテストを同じパッケージ名にしない。個別にインストールできなくなる。
• 各Classのpackage名とは別なので混同しない。
2011年5月26日木曜日
![Page 22: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/22.jpg)
Why?
2011年5月26日木曜日
![Page 23: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/23.jpg)
Android Testing Frameworkは、エミュレータ/実機にインストールされ実行されるアプリケーション ・・なので、
2011年5月26日木曜日
![Page 24: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/24.jpg)
Android Testing Frameworkは、エミュレータ/実機にインストールされ実行されるアプリケーション ・・なので、
実行まで、ちょっと時間がかかります・・・
2011年5月26日木曜日
![Page 25: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/25.jpg)
Android Testing Frameworkは、エミュレータ/実機にインストールされ実行されるアプリケーション ・・なので、
• 製品とテストを同じパッケージ名にしない。個別にインストールできなくなる。
• 各Classのpackage名とは別なので混同しない。こちらは同じpackage名にすることを推奨。
2011年5月26日木曜日
![Page 26: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/26.jpg)
TestCaseを書く
2011年5月26日木曜日
![Page 27: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/27.jpg)
Test Cases (1/2)• AndroidTestCase
• ApplicationTestCase
• LoaderTestCase
• ProviderTestCase2
• ServiceTestCase
※すべて、JUnitのTestCaseのサブクラス。いずれかをテストの 目的によって使い分ける。
2011年5月26日木曜日
![Page 28: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/28.jpg)
Test Cases (1/2)• AndroidTestCase
• ApplicationTestCase
• LoaderTestCase
• ProviderTestCase2
• ServiceTestCase
※すべて、JUnitのTestCaseのサブクラス。いずれかをテストの 目的によって使い分ける。
今のTestterにはこの辺を使う機能がない!
2011年5月26日木曜日
![Page 29: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/29.jpg)
Test Cases (2/2)• InstrumentationTestCase
• ActivityTestCase
• ActivityInstrumentationTestCase2
• ActivityUnitTestCase
• SingleLaunchActivityTestCase
• SyncBaseInstrumentation
2011年5月26日木曜日
![Page 30: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/30.jpg)
AndroidTestCase
2011年5月26日木曜日
![Page 31: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/31.jpg)
AndroidTestCase
• ロジックのテストに使う(非Activity)
• Contextを返す getContext() が利用可能(製品側のContextを得られる)
2011年5月26日木曜日
![Page 32: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/32.jpg)
public class PreferenceLogicTest extends AndroidTestCase {
public void testWriteReadToken() { PreferenceLogic logic = PreferenceLogic.getInstance();
logic.writeToken(getContext(), new AccessToken("11111", "22222")); AccessToken token = logic.readToken(getContext());
assertEquals("11111", token.getToken()); assertEquals("22222", token.getTokenSecret()); }
}
2011年5月26日木曜日
![Page 33: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/33.jpg)
ActivityInstrumentationTestCase2
2011年5月26日木曜日
![Page 34: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/34.jpg)
ActivityInstrumentationTestCase2
• Activityのテストに使う
• Activityの遷移を伴なうテストも可能。(今回は説明しません)
• Contextを返す getContext() が利用可能
• ActivityInstrumentationTestCaseは@Deprecated
2011年5月26日木曜日
![Page 35: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/35.jpg)
package jp.group.android.atec.testter;
public class AuthActivityTest extends ActivityInstrumentationTestCase2<AuthActivity> {
protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mIntent = new Intent(); setActivityIntent(mIntent); mActivity = getActivity(); mEditText = (EditText) mActivity.findViewById(R.id.pinEdit); mButton = (Button) mActivity.findViewById(R.id.registBtn); }
2011年5月26日木曜日
![Page 36: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/36.jpg)
package jp.group.android.atec.testter;
public class AuthActivityTest extends ActivityInstrumentationTestCase2<AuthActivity> {
protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mIntent = new Intent(); setActivityIntent(mIntent); mActivity = getActivity(); mEditText = (EditText) mActivity.findViewById(R.id.pinEdit); mButton = (Button) mActivity.findViewById(R.id.registBtn); }
Activityを生成
コンポーネントを取得製品packageのres
2011年5月26日木曜日
![Page 37: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/37.jpg)
public void testRegistBtnText() {// 登録ボタンのテキストが指定のものであることを確認する assertTrue(mButton.getText().toString() .equalsIgnoreCase("登録"));}
ふつうにAssertionで確認
テストメソッドでは、
2011年5月26日木曜日
![Page 38: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/38.jpg)
コンポーネントの操作も可能※但し、UIスレッドに限る
public void testClickButton() {
mActivity.runOnUiThread(new Runnable() { public void run() { try { mButton.performClick(); assertXxx(...); } catch (RuntimeException e) { assertXxx(...); } } });
}2011年5月26日木曜日
![Page 39: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/39.jpg)
privateを扱う
2011年5月26日木曜日
![Page 40: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/40.jpg)
製品コードの中のprivateなフィールド、メソッド
private final PreferenceLogic preferenceLogic = PreferenceLogic.getInstance();
private Drawable getCacheDrawable(URL url) { (snip)}
2011年5月26日木曜日
![Page 41: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/41.jpg)
リフレクションで取得することは可能
Class<AuthActivity> clazz = AuthActivity.class;Field field = clazz .getDeclaredField("preferenceLogic");
Method method = getClass() .getMethod("getCacheDrawable");method.invoke();
ですが、
2011年5月26日木曜日
![Page 42: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/42.jpg)
素人め、間合いが遠いわ!CV:井芹さん
2011年5月26日木曜日
![Page 43: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/43.jpg)
リフレクションの使用は Fragile Test の一因となる(テストのメンテナンスコストが上がる)ため、
private final PreferenceLogic preferenceLogic = PreferenceLogic.getInstance();
private Drawable getCacheDrawable(URL url) { (snip)}
製品コードをテストしやすく修正する。可視性をdefault(Package Private)にする等。
2011年5月26日木曜日
![Page 44: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/44.jpg)
※実際には、こんなテストをしたかった
Class<View> clazz = View.class;Field field = clazz.getDeclaredField("mOnClickListener");field.setAccessible(true);assertTrue(field.get(mButton) != null);
AndroidSDKの中のClassの、不可視なfield
→このケースは、Mockを使用する?
2011年5月26日木曜日
![Page 45: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/45.jpg)
例外のテスト
2011年5月26日木曜日
![Page 46: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/46.jpg)
プロダクトコード
public AccessToken auth(String pin) { AccessToken accessToken = null; try { (snip) } catch (TwitterException e) { (snip) throw new RuntimeException(e); } return accessToken;}
例外発生==異常系をテストしたい
2011年5月26日木曜日
![Page 47: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/47.jpg)
テストコード
try { target.auth(null); fail("Twitter#getOAuthAccessToken()で 例外が投げられるべき");} catch (RuntimeException e) { assertEquals("catchした例外の中身は正しい", expected, e.getCause());}
例外が発生しなければfail
正しい(想定通りの)例外か確認
2011年5月26日木曜日
![Page 48: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/48.jpg)
Android Mock
2011年5月26日木曜日
![Page 49: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/49.jpg)
Android Mockとは
• Dalvk上で使用できるEasyMockのラッパーです。
• Mockとは、本物のClassの振る舞い(メソッドの戻り値など)をエミュレートすることができる。
2011年5月26日木曜日
![Page 50: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/50.jpg)
製品コード
public AccessToken auth(String pin) { AccessToken accessToken = null; try { if (pin == null || "".equals(pin.trim())) { accessToken = twitter.getOAuthAccessToken(); } else { (snip) } } catch (TwitterException e) { (snip) } (snip)}
Twitter4Jのインスタンス
このメソッドの戻り値を変えたい2011年5月26日木曜日
![Page 51: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/51.jpg)
テストコード(正常系・1/2)
@UsesMocks(Twitter.class)public void testAuth_正常系_PINがnull() { // setup mock AccessToken expectedToken = new AccessToken("testToken", "testSecret");
Twitter twitterMock = AndroidMock.createMock(Twitter.class);
AndroidMock.expect(twitterMock .getOAuthAccessToken()).andReturn(expectedToken);
AndroidMock.replay(twitterMock);
アノテーションで宣言
Twitter型のMockを生成
メソッドに対する戻り値を設定
必要!2011年5月26日木曜日
![Page 52: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/52.jpg)
テストコード(正常系・2/2) // setup test target TwitterLogic target = new TwitterLogic(); target.twitter = twitterMock;
// do test. AccessToken actual = target.auth(null); assertEquals("返されるAccessTokenは正しい", expectedToken, actual);
// 呼ばれたMockメソッドの正当性を検証。 AndroidMock.verify(twitterMock);}
Mockをテスト対象にセット
Mockに定義したメソッドを呼ばれたかチェック
2011年5月26日木曜日
![Page 53: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/53.jpg)
テストコード(異常系)
@UsesMocks(Twitter.class)public void testAuth_異常系_認証失敗() { // setup mock TwitterException expected = new TwitterException("Unauthorized", null, 401); Twitter twitterMock = AndroidMock .createMock(Twitter.class); AndroidMock.expect(twitterMock .getOAuthAccessToken()).andThrow(expected);
AndroidMock.replay(twitterMock);
メソッドが呼ばれたら例外発生
2011年5月26日木曜日
![Page 54: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/54.jpg)
良いテストコードを書こう!
http://www.slideshare.net/goyoki/an-approach-to-improving-the-maintainability-of-unit-tests-xpjugkansai2011
2011年5月26日木曜日
![Page 55: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/55.jpg)
まとめ
• backlogのwiki嫁
• 井芹さんのスライド嫁
2011年5月26日木曜日
![Page 56: Testterを叩け!](https://reader033.vdocuments.site/reader033/viewer/2022051817/5480476db4af9faa158b5ca0/html5/thumbnails/56.jpg)
ご静聴ありがとうございました!
2011.05.25 20:01
2011年5月26日木曜日