Upload
koji-hasegawa
View
5.623
Download
3
Embed Size (px)
DESCRIPTION
Android Test Casual Talks #1 LT資料 http://www.zusaar.com/event/1917003
Citation preview
Androidで使える モックフレームワーク
Android Test Casual Talks #1 LT資料
2013.12.13 長谷川 孝二 @nowsprinting
自己紹介長谷川 孝二 @nowsprinting (twitter, github, etc...)
• テスト自動化研究会
• Androidテスト部
• iOS/Androidアプリ開発者
• 受託開発(フリーランス)
• 自社開発
アジェンダ• モックとは • Dalvik上で動作するモックFWの変遷
• モックフレームワークの使用例 • EasyMock
• Mockito
モックとは
xUnit Test Patternsでの定義
#11 Using Test Doubles
#23 Test Double Patterns
xUnit Test Patternsでの定義
• Test Double == 身代わり、影武者の意
• Test Stub(間接入力を操作する)
• Test Spy(間接出力を記録する)
• Mock Object(間接出力の検証もする)
• Fake Object(偽の間接入力を返す)
• Dummy Object(nullじゃない程度)
テストの基本 SUTへの入力に対する 出力を検証する
依存オブジェクトがあると難しくなる
依存オブジェクト
• Aさん担当クラス(進捗どうですか? これバグじゃありませんか?)
• Date, Calendar, Random(再現性)
• HttpClient(例外、通信エラー、正常系でも再現性の確保)
Test Stub/Fake Objectで 間接入力を固定化
Test Spy/Mock Objectで 間接出力を検証する
• xUnit Test Patterns(洋書) http://xunitpatterns.com/ http://www.fieldnotes.jp/xutp/ ( 読書会の記録)
• 井芹さんのエントリ xUnit Test PatternsのTest Doubleパターン(Mock,Stub,Fake,Dummy等の定義)
http://goyoki.hatenablog.com/entry/20120301/1330608789
詳しくは…
モックフレームワークとは
• Test Double == 身代わり、影武者の意
• Test Stub(間接入力を操作する)
• Test Spy(間接出力を記録する)
• Mock Object(間接出力の検証もする)
• Fake Object(偽の間接入力を返す)
• Dummy Object(nullじゃない程度)
全部できる! しかも動的に
Dalvik上で動く モックフレームワークの変遷
Android Mock
• 2009年、Dalvikチームが4週間で作った
• EasyMockベース
• 設定めんどくさい • @IT記事
Android Mockを利用してHTTP通信をテストするには http://www.atmarkit.co.jp/ait/articles/1204/13/news144.html
その半年後…
Android Mock
• 2012年11月、開発・サポート終了
• https://code.google.com/p/mockito/
• MIT License
• 1.9.5rc-1(03-06-2012)でDalvikサポート
(動作にはdexmakerが必要)
• Strict Mockは無く、間接出力の厳密な 検証はInOrderを使って行なう
• http://easymock.org/
• Apache License 2.0
• 3.2(2013-07-11)でDalvikサポート
(動作にはdexmakerが必要)
• Nice Mock, Mock, Strict Mockの三種類で 検証の厳密さが異なる
モックフレームワークの使用例 EasyMock編
EasyMockの設定• easymock-3.2.jarを入手、libs/に格納
http://easymock.org/Downloads.html
• dexmaker-1.0.jarを入手、libs/に格納 http://code.google.com/p/dexmaker/
• Build Pathにeasymock-3.2.jarを追加
モックの使用例 1/2// HTTPステータス500を返すStatusLineモックStatusLine mockStatusLine = EasyMock.createMock(StatusLine.class);EasyMock.expect(mockStatusLine.getStatusCode()) .andReturn(500);EasyMock.replay(mockStatusLine);!(snip)!// テスト対象のインスタンスフィールドにある// HttpClientをモックに差し替えるmFxRateLoader.mHttpclient = mockHttpClient;
モックの使用例 2/2// テスト実行try { mFxRateLoader.requestFeed(); fail();!} catch (InvalidHttpStatusCodeException e) { // 例外がスローされるのが正しい振る舞い // ステータスコードが正しいか検証 assertEquals(500, e.getStatusCode());}
例外発生のテスト 1/2// 通信できない状態の例外をスローするHttpClientモックException expected = new UnknownHostException();HttpClient mockHttpClient = EasyMock.createMock(HttpClient.class);EasyMock.expect(mockHttpClient.execute((HttpUriRequest)EasyMock.notNull())).andThrow(expected);
EasyMock.replay(mockHttpClient);!// テスト対象のインスタンスフィールドにある// HttpClientをモックに差し替えるmFxRateLoader.mHttpclient = mockHttpClient;
例外発生のテスト 2/2
// テスト実行try { mFxRateLoader.requestFeed(); fail("UnknownHostExceptionがthrowされていない");!} catch (UnknownHostException e) {
// 例外がスローされるのが正しい振る舞い}
StrictMockの例 1/2// ダミーのパース結果を返すXmlPullParserモックXmlPullParser mockParser = EasyMock.createStrictMock(XmlPullParser.class);mockParser.setInput((InputStream)EasyMock.notNull(), (String)EasyMock.notNull());EasyMock.expect(mockParser.getEventType()).andReturn(XmlPullParser.START_TAG);EasyMock.expect(mockParser.getName()).andReturn("gesmes:Envelope");// 無視されるEasyMock.expect(mockParser.next()).andReturn(XmlPullParser.START_TAG);(snip)EasyMock.expect(mockParser.getName()).andReturn("Cube");// USDEasyMock.expect(mockParser.getAttributeValue(null, "currency")).andReturn("USD");EasyMock.expect(mockParser.getAttributeValue(null, "rate")).andReturn(SEED_EURUSD);EasyMock.expect(mockParser.next()).andReturn(XmlPullParser.START_TAG);(snip)EasyMock.expect(mockParser.next()).andReturn(XmlPullParser.END_TAG); // </gesmes:Envelope>EasyMock.expect(mockParser.next()).andReturn(XmlPullParser.END_DOCUMENT);EasyMock.replay(mockParser);!// テスト対象のインスタンスフィールドにあるHttpClientをモックに差し替えるmFxRateLoader.mParser = mockParser;
StrictMockの例 2/2
// テスト実行InputStream dummyResponseBody = createDummyInputStream();FxRate actual = mFxRateLoader.parseFeed(dummyResponseBody);assertEquals("EUR/USDは正しくパースされている", expectedEurusd, actual.eurusd);assertEquals("EUR/JPYは正しくパースされている", expectedEurjpy, actual.eurjpy);
StrictMockの例 2/2
// テスト実行InputStream dummyResponseBody = createDummyInputStream();FxRate actual = mFxRateLoader.parseFeed(dummyResponseBody);assertEquals("EUR/USDは正しくパースされている", expectedEurusd, actual.eurusd);assertEquals("EUR/JPYは正しくパースされている", expectedEurjpy, actual.eurjpy);
parseFeed()内でStrictMockに定義した順にメソッドを呼ばれていないと、その場で例外発生
Mockの乱用は危険• 実装のテストになりがち →何をテストしているのか?
• 実装の変更に弱い →壊れやすいテスト(Fragile Test)
→保守の放棄
モックフレームワークの使用例 Mockito編
買って 買って 買って 買って
買って 買って 買って 買って
買って 買って 買って 買って
買って 買って 買って 買って
買って 買って 買って 買って
買って 買って 買って 買って
ご清聴ありがとう ございました