Your SlideShare is downloading. ×
Unit test in android
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Unit test in android

529

Published on

Unit test in Android using Robolectric.

Unit test in Android using Robolectric.

Published in: Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
529
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
1
Comments
0
Likes
4
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. ユニットテスト入門 Androidアプリケーションへのユニットテスト導入
  • 2. Tatsuya Maki Android Application Developer
  • 3. はなすこと 1. ユニットテストの導入 2. 効率的なユニットテスト 3. ユニットテストのTips
  • 4. ユニットテストの導入 1
  • 5. Q. ユニットテストとは
  • 6. A. メソッドなど小さな単位で行うテスト
  • 7. public static String fizzbuzz(int value) { String message; if (value % 15 == 0) { message = "FizzBuzz"; } else if (value % 5 == 0) { message = "Buzz"; } else if (value % 3 == 0) { message = "Fizz"; } else { message = String.valueOf(value); } return message; } FizzBuzz
  • 8. @Test public void fizzbuzzShouldReturnBuzzWhenValueIs10() { // 実行結果 String actualValue = fizzbuzz(10); ! // 期待結果 String expectedValue = "Buzz"; ! // テスト assertThat(actualValue, is(expectedValue)); } FizzBuzzTest
  • 9. Androidでの問題点 1. デバイスが必須 2. 実行速度が遅い 3. テストしにくい
  • 10. Robolectric http://robolectric.org/
  • 11. 遅い DalvikVM 必要 JUnit3 速い JVM 不要 JUnit4 AndroidTestCase 実行速度 VM デバイス JUnit Robolectric
  • 12. TextView Shadow getText() Hello, world.
  • 13. @Implements(TextView.class) class MyShadowTextView extends ShadowView { ! @Implementation public CharSequence getText() { return "Hello, Robolectric!"; } ! } Shadowの例
  • 14. @Test @Config(shadows = { MyShadowTextView.class }) public void getTextShouldReturnHelloRobolectric() { // SetUp Context context = Robolectric.application.getApplicationContext(); TextView textView = new TextView(context); // Exercise CharSequence actualText = textView.getText(); // Verify assertEquals(actualText, "Hello, Robolectric!"); } Shadowの利用例
  • 15. まとめ 1. デバイスが不要 2. JVM上で動作する 3. Shadowオブジェクト
  • 16. 効率的なユニットテスト 2
  • 17. 3つのツール 1. FEST 2. Mockito 3. EclEmma
  • 18. FEST https://code.google.com/p/fest/
  • 19. Q.アサーションとは
  • 20. A. 実行結果と期待結果を比較検証する宣言
  • 21. // 実行結果 int actualValue = 10 / 2; ! // 期待結果 int expectedValue = 5; ! // "10 / 2"が"5"と等しくなることを表明 assertEquals(actualValue, expectedValue); アサーションの例
  • 22. FESTのメリット 1. アサーションの記述が容易 2. エラーメッセージが明確 3. Androidと親和性が高い
  • 23. アサーションの記述が容易
  • 24. // Exercise List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); ! ! // Verify assertNotNull(devices); assertEquals(2, devices.size()); assertTrue(devices.contains("Nexus 5")); JUnit
  • 25. // Exercise List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); ! ! // Verify assertThat(devices, is(notNullValue())); assertThat(devices.size(), is(equalTo(2))); assertThat(devices, hasItem("Nexus 5")); Hamcrest
  • 26. // Exercise List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); ! ! // Verify assertThat(devices) .isNotNull() .hasSize(2) .contains("Nexus 5"); FEST
  • 27. エラーメッセージが明確
  • 28. // テストコード List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); assertTrue(devices.contains("Nexus 4")); ! ! // エラーメッセージ java.lang.AssertionError ! ! ! ! JUnit
  • 29. // テストコード List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); assertThat(devices, hasItem("Nexus 4")); ! ! // エラーメッセージ java.lang.AssertionError: Expected: a collection containing "Nexus 4" but: was "Nexus 5", was "Nexus 7” ! ! Hamcrest
  • 30. // テストコード List<String> devices = new ArrayList<String>(); devices.add("Nexus 5"); devices.add("Nexus 7"); assertThat(devices).contains("Nexus 4"); ! ! // エラーメッセージ java.lang.AssertionError: expecting: <['Nexus 5', 'Nexus 7']> to contain: <['Neuxus 4']> but could not find: <['Neuxus 4']> FEST
  • 31. Androidと親和性が高い
  • 32. // テストコード TextView textView = (TextView) activity .findViewById(R.id.text_view); assertThat(textView.getText()) .isEqualTo(activity.getString(R.string.message)); assertThat(textView.getVisibility()) .isEqualTo(View.VISIBLE); ! ! // エラーメッセージ org.junit.ComparisonFailure: expected: <[0]> but was: <[8]> FEST
  • 33. // テストコード TextView textView = (TextView) activity .findViewById(R.id.text_view); assertThat(textView) .hasText(R.string.message) .isVisible(); ! ! ! // エラーメッセージ java.lang.AssertionError: Expected to be visible but was gone ! FEST Android
  • 34. Mockito http://mockito.org/
  • 35. Q. モックオブジェクトとは
  • 36. A. オブジェクトの呼び出しを検証する
  • 37. class MockInputStream extends InputStream { ! private boolean mIsRead; private boolean mIsClosed; ! @Override public int read() throws IOException { mIsRead = true; return 0; } ! public boolean isRead() { return mIsRead; } ! @Override public void close() throws IOException { mIsClosed = true; } ! public boolean isClosed() { return mIsClosed; } ! } モックオブジェクトの例
  • 38. Mockitoでできること 1. 呼び出しの検証 2. 振る舞いの変更 3. フィールドの変更
  • 39. // モックオブジェクトの生成 InputStream mocked = mock(InputStream.class); // InputStreamをクローズする mocked.close(); ! // closeメソッドの呼び出し検証 verify(mocked).close(); 呼び出しの検証
  • 40. 振る舞いの変更 // モックオブジェクトの作成 InputStream mocked = mock(InputStream.class); ! // 戻り値の変更 when(mocked.read()).thenReturn(-1); ! // 例外の送出 when(mocked.read()).thenThrow(new IOException()); ! // ロジックの変更 when(mocked.read()).thenAnswer(new Answer<Integer>() { ! @Override public Integer answer(InvocationOnMock invocation) throws Throwable { int length; ... return length; } });
  • 41. フィールドの変更 // 内部にInputStreamを保持するクラス MyObject object = new MyObject(); // フィールドの取得 InputStream stream = (InputStream) Whitebox .getInternalState(object, “mStream"); ! // スパイオブジェクトの作成 Socket spied = spy(stream); ! // フィールドの変更 Whitebox .setInternalState(object, "mStream", spied);
  • 42. Mockitoでできないこと 1. finalクラス/メソッドのモック 2. privateメソッドのモック 3. staticメソッドのモック
  • 43. EclEmma http://www.eclemma.org/
  • 44. Q. カバレッジとは
  • 45. A. テストがどれだけ網羅できているか
  • 46. カバレッジの種類 C0: 命令網羅率 C1: 分岐網羅率 C2: 条件網羅率
  • 47. EclEmmaで測定できるもの C0: 命令網羅率 C1: 分岐網羅率 C2: 条件網羅率
  • 48. EclEmmaで測定できないもの C0: 命令網羅率 C1: 分岐網羅率 C2: 条件網羅率
  • 49. ユニットテストのTips 3
  • 50. Q. void型メソッドをテストしたい
  • 51. A. 別のメソッドを使って検証する
  • 52. List<String> list = new MyList<String>(); list.add("Android"); // 別のメソッドで検証 assertThat(list).hasSize(1); サンプル
  • 53. A. メソッドの呼び出しを検証する
  • 54. InputStream mocked = mock(InputStream.class); IoUtils.close(mocked); ! // closeメソッドの呼び出し検証 verify(mocked).close(); サンプル
  • 55. Q. privateメソッドをテストしたい
  • 56. A. 諦める
  • 57. A. package privateに変更する
  • 58. // privateメソッドなので呼び出せない private String buildMessage() { String message; // do something return message; } ! // package privateに変更 String buildMessage() { String message; // do something return message; } サンプル
  • 59. Q. 非同期処理をテストしたい
  • 60. A. CountDownLatchを使う
  • 61. // timeoutを指定 @Test(timeout = 1000) public void test() throws InterruptedException{ CountDownLatch latch = new CountDownLatch(1); AsyncProcess.execute(new Callback() { @Override public void onComplete() { // 処理完了時にcountDownを呼出 latch.countDown(); } }); ! // 処理完了まで待機 latch.await(); ! ... } サンプル
  • 62. Q. HTTP通信の外部依存をなくしたい
  • 63. A. FakeHttpLayerを使う HttpClientの場合
  • 64. // 常に同じレスポンスを返却 FakeHttpLayer layer = Robolectric.getFakeHttpLayer(); layer.setDefaultHttpResponse(200, "Hello, World!"); ! ! // 順番にレスポンスを変更 FakeHttpLayer layer = Robolectric.getFakeHttpLayer(); layer.addPendingHttpResponse(200, "Hello, Nexus 4!"); layer.addPendingHttpResponse(200, "Hello, Nexus 5!"); layer.addPendingHttpResponse(200, "Hello, Nexus 7!"); ! ! // 動的にレスポンスを変更 FakeHttpLayer layer = Robolectric.getFakeHttpLayer(); layer.addHttpResponseRule(new MyResponseRule()); サンプル
  • 65. A. URLStreamHandlerを設定する HttpURLConnectionの場合
  • 66. class StubURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { // HttpURLConnectionを作成 return new StuHttpURLConnection(url); } } ! class StubURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler( String protocol) { // URLStreamHandlerを作成 return new StubURLStreamHandler(); } } ! // URLStreamHandlerFactoryを設定 URL.setURLStreamHandlerFactory( new StubURLStreamHandlerFactory()); サンプル
  • 67. Q. データベースのスローテストを解消したい
  • 68. A. インメモリデータベースを使う
  • 69. // DatabaseMapのinterfaceを実装 class MemoryDatabaseMap implements DatabaseMap { ! @Override public String getDriverClassName() { return JDBC.class.getName(); } ! @Override public String getConnectionString(File file) { // インメモリデータベースを使うように指定 return "jdbc:sqlite::memory:"; } ! @Override public String getMemoryConnectionString() { // インメモリデータベースを使うように指定 return "jdbc:sqlite::memory:"; } ! @Override public int getResultSetType() { return ResultSet.TYPE_FORWARD_ONLY; } ! @Override public String getSelectLastInsertIdentity() { return "SELECT last_insert_rowid() AS id"; } ! } サンプル
  • 70. // UsingDatabaseMapで指定 @UsingDatabaseMap(MemoryDatabaseMap.class) @RunWith(RobolectricTestRunner.class) class DatabaseHelperTest { ... } サンプル
  • 71. Q. Activityをテストしたい
  • 72. A. ActivityControllerを使う
  • 73. // ActivityControllerを生成 ActivityController<MainActivity> controller = Robolectric.buildActivity(MainActivity.class); ! // Activityの生成 controller.create().start().resume().visible(); ! // Activityの取得 MainActivity activity = controller.get(); // Activityの操作 TextView textView = (TextView) activity .findViewById(R.id.text_view); ... ! // Activityの破棄 controller.pause().stop().destroy(); サンプル
  • 74. Q. Serviceを検証したい
  • 75. A. ライフサイクルに合わせてメソッドを呼び出す
  • 76. private MyService mService; ! @Before public void setUp() { // Serviceを生成 mService = new MyService(); mService.onCreate(); } @Test public void test() { // Serviceを実行 Intent intent = new Intent(Intent.ACTION_SEARCH); intent.putExtra(SearchManager.QUERY, "Hello, World!"); mService.onStartCommand(intent, 0, 0); } ! @After public void tearDown() { // Serviceを破棄 mService.onDestroy(); } サンプル
  • 77. Q. Widgetのテストをしたい
  • 78. A. ShadowAppWidgetManagerを使う
  • 79. // ShadowAppWidgetManagerの生成 Context context = Robolectric.application .getApplicationContext(); AppWidgetManager manager = AppWidgetManager.getInstance(context); ShadowAppWidgetManager shadowManager = Robolectric.shadowOf(manager); // Widgetの生成 int widgetId = shadowManager.createWidget( MyWidgetProvider.class, R.layout.activity_main); // Viewの取得 View widgetView = shadowManager.getViewFor(widgetId); ! ... サンプル
  • 80. Q. ContentProviderのテストをしたい
  • 81. A. ShadowContentResolverを使う
  • 82. // ShadowContentResolverの生成 ContentResolver resolver = Robolectric.application.getContentResolver(); ShadowContentResolver shadowResolver = Robolectric.shadowOf(resolver); ! // ContentProviderの登録 shadowResolver.registerProvider( “com.example.android.unittest” new MyContentProvider()); ... // INSERTステートメントの取得 List<InsertStatement> insertStatements = shadowResolver.getInsertStatements(); // UPDATEステートメントの取得 List<UpdateStatement> updateStatements = shadowResolver.getUpdateStatements(); // DELETEステートメントの取得 List<DeleteStatement> deleteStatements = shadowResolver.getDeleteStatements(); // notifyChangeで通知されたURIの取得 List<NotifiedUri> notifiedUris = shadowResolver.getNotifiedUris(); サンプル
  • 83. Q. BroadcastReceiverのテストをしたい
  • 84. A. ShadowApplicationを使う
  • 85. // 事前処理 ShadowApplication shadowApplication = Robolectric.getShadowApplication(); Context context = Robolectric.application.getApplicationContext(); // 登録済みBroadcastReceiverの取得 List<Wrapper> registered = shadowApplication.getRegisteredReceivers(); // Intentに該当するBroadcastReceiverの取得 Intent intent = new Intent("MY_ACTION"); List<BroadcastReceiver> receivers = shadowApplication.getReceiversForIntent(intent); // Intentを擬似的に受信 BroadcastReceiver receiver = receivers.get(0); receiver.onReceive(context, intent); サンプル
  • 86. まとめ 4
  • 87. まとめ 1. 課題はRobolectricで解消できる 2. 既存のライブラリを使って効率的に 3. 大体テストできるので書いてみよう
  • 88. Q. おすすめの本はなんですか
  • 89. https://github.com/t28hub/UnitTestInAndroid/

×