2011.05.25 ATEC Mtg#7




2011   5   26
• Twitter id @nowsprinting
                •
                •
                •        Java, Swing, Do-Ja, iOS   SIer/



2011   5   26
Agenda

                •
                • JUnit
                • Android Testing Framework
                • Android Mock

2011   5   26
2011   5   26
©snsk   at ATEC Mtg#1
2011   5   26
©snsk   at ATEC Mtg#1
2011   5   26
2011   5   26
Activity
                        Http



                Lib   (Mock)
                Lib   (Mock)


2011   5   26
→



                                          Monkey
                                                 God Hand
                                          Runner
                              Activity
                               Http



                   Lib       (Mock)
                   Lib       (Mock)

                Robolectric AndroidTest
2011   5   26
→
                                   Android
                                              Activity
                                           Monkey
                                             →    God Hand
                                           Runner
                                         →suspend→resume
                              Activity
                               Http



                   Lib       (Mock)
                   Lib       (Mock)

                Robolectric AndroidTest
2011   5   26
→



                                               Monkey
                Instrumental                          God Hand
                                               Runner
                 TestCase
                                    Activity
                                     Http



                      Lib          (Mock)
                      Lib          (Mock)

                  Robolectric AndroidTest
2011   5   26
JUnit

                • Android Testing Framework   JUnit3

                • TestCase
                • TestCase        Assertion




2011   5   26
TestCase


           package jp.group.android.atec.testter.logic;

           public class TwitterLogic {
           }


                                         package

           package jp.group.android.atec.testter.logic;

           public class TwitterLogicTest
                              extends AndroidTestCase {
           }
           Class          ”Test”
                                         JUnit   TestCase
2011   5   26
“test” +          +

 public void testTwitterLogicAuthorization() {
   Authorization auth4test = ...

       TwitterLogic target = new TwitterLogic(auth4test);

       assertNotNull("Twitter                          ",
                target.twitter);
 }
           Assertion




2011   5   26
assert()

2011   5   26
2011   5   26
Android Testing
                 Framework



2011   5   26
Test Project



2011   5   26
eclipse           package explorer




                          New Test Project...


2011   5   26
Dialog (1/2)
       TestProject




                                    Project




2011   5   26
Dialog (2/2)




                                      package

                •

                •   Class package

2011   5   26
Why?



2011   5   26
Android Testing Framework   /




2011   5   26
Android Testing Framework   /




2011   5   26
Android Testing Framework    /




           •

           •    Class package
                          package




2011   5   26
TestCase



2011   5   26
Test Cases (1/2)
                • AndroidTestCase
                   • ApplicationTestCase
                   • LoaderTestCase
                   • ProviderTestCase2
                   • ServiceTestCase
           ※        JUnit TestCase



2011   5   26
Test Cases (1/2)
                • AndroidTestCase
                   • ApplicationTestCase
                   • LoaderTestCase        Testter
                   • ProviderTestCase2
                   • ServiceTestCase
           ※        JUnit TestCase



2011   5   26
Test Cases (2/2)
                • InstrumentationTestCase
                    • ActivityTestCase
                        • ActivityInstrumentationTestCase2
                        • ActivityUnitTestCase
                    • SingleLaunchActivityTestCase
                    • SyncBaseInstrumentation

2011   5   26
AndroidTestCase



2011   5   26
AndroidTestCase

                •                             Activity

                • Context      getContext()
                            Context




2011   5   26
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
ActivityInstrumentation
                       TestCase2


2011   5   26
ActivityInstrumentation
                       TestCase2
                • Activity
                • Activity

                • Context         getContext()

                • ActivityInstrumentationTestCase
                  @Deprecated

2011   5   26
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
package jp.group.android.atec.testter;

public class AuthActivityTest extends
    ActivityInstrumentationTestCase2<AuthActivity> {

       protected void setUp() throws Exception {
         super.setUp();
         setActivityInitialTouchMode(true);
         mIntent = new Intent();            Activity
         setActivityIntent(mIntent);
         mActivity = getActivity();
         mEditText = (EditText)
                  mActivity.findViewById(R.id.pinEdit);
         mButton = (Button)
                  mActivity.findViewById(R.id.registBtn);
       }
                                            package res
2011   5   26
public void testRegistBtnText() {
  //
           assertTrue(mButton.getText().toString()
                                 .equalsIgnoreCase("   "));
  }
                        Assertion




2011   5   26
※       UI
            public void testClickButton() {

                mActivity.runOnUiThread(new Runnable() {
                  public void run() {
                    try {
                      mButton.performClick();
                      assertXxx(...);
                    } catch (RuntimeException e) {
                      assertXxx(...);
                    }
                  }
                });

            }
2011   5   26
private



2011   5   26
private



       private final PreferenceLogic preferenceLogic
                       = PreferenceLogic.getInstance();



       private Drawable getCacheDrawable(URL url) {
         (snip)
       }




2011   5   26
Class<AuthActivity> clazz = AuthActivity.class;
       Field field = clazz
                  .getDeclaredField("preferenceLogic");



       Method method = getClass()
                        .getMethod("getCacheDrawable");
       method.invoke();




2011   5   26
CV




2011   5   26
Fragile Test



       private final PreferenceLogic preferenceLogic
                       = PreferenceLogic.getInstance();



       private Drawable getCacheDrawable(URL url) {
         (snip)
       }




                  default Package Private

2011   5   26
※

           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
2011   5   26
public AccessToken auth(String pin) {
                  AccessToken accessToken = null;
                  try {
                    (snip)
                  } catch (TwitterException e) {
                    (snip)
                    throw new RuntimeException(e);
                  }
                  return accessToken;
                }

                                    ==



2011   5   26
fail
           try {
             target.auth(null);
             fail("Twitter#getOAuthAccessToken()
                                                        ");
           } catch (RuntimeException e) {
             assertEquals("catch                   ",
                               expected, e.getCause());
           }




2011   5   26
Android Mock



2011   5   26
Android Mock

                • Dalvk      EasyMock



                • Mock     Class




2011   5   26
public AccessToken auth(String pin) {
         AccessToken accessToken = null;
           try {
             if (pin == null || "".equals(pin.trim())) {
                accessToken =
                         twitter.getOAuthAccessToken();
             } else {
                (snip)
             }
         } catch (TwitterException e) {
           (snip)
         }          Twitter4J
         (snip)
       }


2011   5   26
1/2

 @UsesMocks(Twitter.class)
 public void testAuth_     _PIN     null() {
       // setup mock
       AccessToken expectedToken
            = new AccessToken("testToken", "testSecret");

                                   Twitter     Mock
       Twitter twitterMock
            = AndroidMock.createMock(Twitter.class);

       AndroidMock.expect(twitterMock
        .getOAuthAccessToken()).andReturn(expectedToken);



       AndroidMock.replay(twitterMock);

2011   5   26
2/2
           // setup test target
           TwitterLogic target = new TwitterLogic();
           target.twitter = twitterMock;
                                 Mock
           // do test.
           AccessToken actual = target.auth(null);
           assertEquals("       AccessToken      ",
                                     expectedToken, actual);

           //          Mock
           AndroidMock.verify(twitterMock);
       }

                Mock

2011   5   26
@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
http://www.slideshare.net/goyoki/an-approach-to-improving-the-maintainability-
                         of-unit-tests-xpjugkansai2011
2011   5   26
• backlog   wiki

                •


2011   5   26
2011.05.25 20:01


2011   5   26

Testterを叩け!

  • 1.
  • 2.
    • Twitter id@nowsprinting • • • Java, Swing, Do-Ja, iOS SIer/ 2011 5 26
  • 3.
    Agenda • • JUnit • Android Testing Framework • Android Mock 2011 5 26
  • 4.
    2011 5 26
  • 5.
    ©snsk at ATEC Mtg#1 2011 5 26
  • 6.
    ©snsk at ATEC Mtg#1 2011 5 26
  • 7.
    2011 5 26
  • 8.
    Activity Http Lib (Mock) Lib (Mock) 2011 5 26
  • 9.
    Monkey God Hand Runner Activity Http Lib (Mock) Lib (Mock) Robolectric AndroidTest 2011 5 26
  • 10.
    Android Activity Monkey → God Hand Runner →suspend→resume Activity Http Lib (Mock) Lib (Mock) Robolectric AndroidTest 2011 5 26
  • 11.
    Monkey Instrumental God Hand Runner TestCase Activity Http Lib (Mock) Lib (Mock) Robolectric AndroidTest 2011 5 26
  • 12.
    JUnit • Android Testing Framework JUnit3 • TestCase • TestCase Assertion 2011 5 26
  • 13.
    TestCase package jp.group.android.atec.testter.logic; public class TwitterLogic { } package package jp.group.android.atec.testter.logic; public class TwitterLogicTest extends AndroidTestCase { } Class ”Test” JUnit TestCase 2011 5 26
  • 14.
    “test” + + public void testTwitterLogicAuthorization() { Authorization auth4test = ... TwitterLogic target = new TwitterLogic(auth4test); assertNotNull("Twitter ", target.twitter); } Assertion 2011 5 26
  • 15.
  • 16.
    2011 5 26
  • 17.
    Android Testing Framework 2011 5 26
  • 18.
  • 19.
    eclipse package explorer New Test Project... 2011 5 26
  • 20.
    Dialog (1/2) TestProject Project 2011 5 26
  • 21.
    Dialog (2/2) package • • Class package 2011 5 26
  • 22.
  • 23.
  • 24.
  • 25.
    Android Testing Framework / • • Class package package 2011 5 26
  • 26.
  • 27.
    Test Cases (1/2) • AndroidTestCase • ApplicationTestCase • LoaderTestCase • ProviderTestCase2 • ServiceTestCase ※ JUnit TestCase 2011 5 26
  • 28.
    Test Cases (1/2) • AndroidTestCase • ApplicationTestCase • LoaderTestCase Testter • ProviderTestCase2 • ServiceTestCase ※ JUnit TestCase 2011 5 26
  • 29.
    Test Cases (2/2) • InstrumentationTestCase • ActivityTestCase • ActivityInstrumentationTestCase2 • ActivityUnitTestCase • SingleLaunchActivityTestCase • SyncBaseInstrumentation 2011 5 26
  • 30.
  • 31.
    AndroidTestCase • Activity • Context getContext() Context 2011 5 26
  • 32.
    public class PreferenceLogicTestextends 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
  • 33.
    ActivityInstrumentation TestCase2 2011 5 26
  • 34.
    ActivityInstrumentation TestCase2 • Activity • Activity • Context getContext() • ActivityInstrumentationTestCase @Deprecated 2011 5 26
  • 35.
    package jp.group.android.atec.testter; public classAuthActivityTest 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
  • 36.
    package jp.group.android.atec.testter; public classAuthActivityTest extends ActivityInstrumentationTestCase2<AuthActivity> { protected void setUp() throws Exception { super.setUp(); setActivityInitialTouchMode(true); mIntent = new Intent(); Activity setActivityIntent(mIntent); mActivity = getActivity(); mEditText = (EditText) mActivity.findViewById(R.id.pinEdit); mButton = (Button) mActivity.findViewById(R.id.registBtn); } package res 2011 5 26
  • 37.
    public void testRegistBtnText(){ // assertTrue(mButton.getText().toString() .equalsIgnoreCase(" ")); } Assertion 2011 5 26
  • 38.
    UI public void testClickButton() { mActivity.runOnUiThread(new Runnable() { public void run() { try { mButton.performClick(); assertXxx(...); } catch (RuntimeException e) { assertXxx(...); } } }); } 2011 5 26
  • 39.
  • 40.
    private private final PreferenceLogic preferenceLogic = PreferenceLogic.getInstance(); private Drawable getCacheDrawable(URL url) { (snip) } 2011 5 26
  • 41.
    Class<AuthActivity> clazz =AuthActivity.class; Field field = clazz .getDeclaredField("preferenceLogic"); Method method = getClass() .getMethod("getCacheDrawable"); method.invoke(); 2011 5 26
  • 42.
  • 43.
    Fragile Test private final PreferenceLogic preferenceLogic = PreferenceLogic.getInstance(); private Drawable getCacheDrawable(URL url) { (snip) } default Package Private 2011 5 26
  • 44.
    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
  • 45.
    2011 5 26
  • 46.
    public AccessToken auth(Stringpin) { AccessToken accessToken = null; try { (snip) } catch (TwitterException e) { (snip) throw new RuntimeException(e); } return accessToken; } == 2011 5 26
  • 47.
    fail try { target.auth(null); fail("Twitter#getOAuthAccessToken() "); } catch (RuntimeException e) { assertEquals("catch ", expected, e.getCause()); } 2011 5 26
  • 48.
  • 49.
    Android Mock • Dalvk EasyMock • Mock Class 2011 5 26
  • 50.
    public AccessToken auth(Stringpin) { AccessToken accessToken = null; try { if (pin == null || "".equals(pin.trim())) { accessToken = twitter.getOAuthAccessToken(); } else { (snip) } } catch (TwitterException e) { (snip) } Twitter4J (snip) } 2011 5 26
  • 51.
    1/2 @UsesMocks(Twitter.class) publicvoid testAuth_ _PIN null() { // setup mock AccessToken expectedToken = new AccessToken("testToken", "testSecret"); Twitter Mock Twitter twitterMock = AndroidMock.createMock(Twitter.class); AndroidMock.expect(twitterMock .getOAuthAccessToken()).andReturn(expectedToken); AndroidMock.replay(twitterMock); 2011 5 26
  • 52.
    2/2 // setup test target TwitterLogic target = new TwitterLogic(); target.twitter = twitterMock; Mock // do test. AccessToken actual = target.auth(null); assertEquals(" AccessToken ", expectedToken, actual); // Mock AndroidMock.verify(twitterMock); } Mock 2011 5 26
  • 53.
    @UsesMocks(Twitter.class) public voidtestAuth_ _ () { // 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
  • 54.
  • 55.
    • backlog wiki • 2011 5 26
  • 56.