Archive

Posts Tagged ‘test’

androidの単体テスト(AndroidTestCase)

12月 29th, 2009

 引き続き単体テストのお話。今回は「AndroidTestCase」に焦点をあてます。ApiDemosでAndroidTestCaseを使用しているのはFocus2AndroidTestです。

・AndroidTestCase
 Focus2AndroidTestのjavadocには以下のように書いてあります。

/**
* This exercises the same logic as {@link Focus2ActivityTest} but in a lighter
* weight manner; it doesn’t need to launch the activity, and it can test the
* focus behavior by calling {@link FocusFinder} methods directly.
*
* {@link Focus2ActivityTest} is still useful to verify that, at an end to end
* level, key events actually translate to focus transitioning in the way we expect.
* A good complementary way to use both types of tests might be to have more exhaustive
* coverage in the lighter weight test case, and a few end to end scenarios in the
* functional {@link android.test.ActivityInstrumentationTestCase}. This would provide reasonable
* assurance that the end to end system is working, while avoiding the overhead of
* having every corner case exercised in the slower, heavier weight way.
*
* Even as a lighter weight test, this test still needs access to a {@link Context}
* to inflate the file, which is why it extends {@link AndroidTestCase}.
*
* If you ever need a context to do your work in tests, you can extend
* {@link AndroidTestCase}, and when run via an {@link android.test.InstrumentationTestRunner},
* the context will be injected for you.
*
* See {@link com.example.android.apis.app.ForwardingTest} for an example of an Activity unit test.
*
* See {@link com.example.android.apis.AllTests} for documentation on running
* all tests and individual tests in this application.
*/

 要約すると、ActivityInstrumentationTestCase2よりライトなテストを行うのがAndroidTestCaseであり、Activityをlaunchすることなく、テストを行えるのが特徴で、FocusFinderクラスを使用することにより、UIに対して直接フォーカス移動等のテストが行える、ということのようです。

 まず、setUpメソッドを見てみます。
・com.example.android.apis.view.Focus2AndroidTest

public class Focus2AndroidTest extends AndroidTestCase {

    private FocusFinder mFocusFinder;

    private ViewGroup mRoot;

    private Button mLeftButton;
    private Button mCenterButton;
    private Button mRightButton;

    @Override
    protected void setUp() throws Exception {
        super.setUp();

        mFocusFinder = FocusFinder.getInstance();

        // inflate the layout
        final Context context = getContext();
        final LayoutInflater inflater = LayoutInflater.from(context);
        mRoot = (ViewGroup) inflater.inflate(R.layout.focus_2, null);

        // manually measure it, and lay it out
        mRoot.measure(500, 500);
        mRoot.layout(0, 0, 500, 500);

        mLeftButton = (Button) mRoot.findViewById(R.id.leftButton);
        mCenterButton = (Button) mRoot.findViewById(R.id.centerButton);
        mRightButton = (Button) mRoot.findViewById(R.id.rightButton);
    }

 ここで行っているのは、FocusFinderのインスタンスを作成し、コンテキストからテスト対象のActivityのViewGroupのレイアウトを設定することです。Activityのライフサイクルを通さないので全部自前でやる必要がある、ということですね。

 テストは以下のようになります。

    @SmallTest
    public void testGoingRightFromLeftButtonJumpsOverCenterToRight() {
        assertEquals("right should be next focus from left",
                mRightButton,
                mFocusFinder.findNextFocus(mRoot, mLeftButton, View.FOCUS_RIGHT));
    }

 FocusFinderによって次のフォーカスに移動させ、その移動先がLeftButtonであることを確認しています。

・ということで。
 フォーカスの簡単な確認であればAndroidTestCaseでテストできますが、Activityのライフサイクルもテストしたい場合はActivityInstrumentationTestCase2でテストする、という形ですね。

taga Android , , ,

androidの単体テスト(ActivityInstrumentationTestCase2)

12月 29th, 2009
  •  androidの単体テストの書き方がよくわからなかったので、ApiDemosを参考にしつつ、javadocとにらめっこして解読してみました。

     まずクラス関係は以下のようになってます。
    instrumentationtestcasee591a8e3828a

     この中でActivityクラスのテストに用いるのが、

    1. ActivityTestCase
    2. ActivityInstrumentationTestCase2
    3. ActivityUnitTestCase

    あたりの様子。今回はActivityInstrumentationTestCase2に注目してみます。

    ・com.example.android.apis.view.Focus2ActivityTest

     ApiDemosのFocus2ActivityTestはActivityInstrumentationTestCaseを継承したテストクラスです。ただ、現状ActivityInstrumentationTestCaseはdeprecatedになっているため、本来はActivityInstrumentationTestCase2にすべきですね。

    public class Focus2ActivityTest extends ActivityInstrumentationTestCase<Focus2> {
        public Focus2ActivityTest() {
            super("com.example.android.apis", Focus2.class);
        }
    

    Focus2クラスに対してのテストのため、上記のように宣言します。
    Focus2は「leftButton」、「centerButton」、「rightButton」の3つのボタンが並んでいるAcitivityです。

    ・AndroidManifest.xml

        <application>
            <uses-library android:name="android.test.runner" />
        </application>
        <instrumentation android:name="android.test.InstrumentationTestRunner"
                         android:targetPackage="com.example.android.apis"
                         android:label="Tests for Api Demos."/>
    

    Manifestファイルにテスト対象のパッケージを登録します。

    ※InstrumentationTestRunnerについてはリファレンスにしっかり書いてあるので、ここでは特に書かないことにします。

        @MediumTest
        public void testGoingRightFromLeftButtonJumpsOverCenterToRight() {
            sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT);
            assertTrue("right button should be focused", mRightButton.isFocused());
        }
    

     右キーを押下したときにフォーカスがrightbuttonに移動していることを確認するテストです。
     ここで使用しているsendKeysメソッドはInstrumentationTestCaseのメソッドです。
     実はメインスレッドでActivityを操作することはできないらしく、このsendKeysメソッドを呼び出したとき、内部でUI操作用のスレッドを立て、そのスレッドでイベントキーを発行し、処理が終わり、アイドル状態になるまでwaitしています。

     それが分かるのが次のテストケースです。

        @MediumTest
        public void testGoingLeftFromRightButtonGoesToCenter()  {
            // Give right button focus by having it request focus.  We post it
            // to the UI thread because we are not running on the same thread, and
            // any direct api calls that change state must be made from the UI thread.
            // This is in contrast to instrumentation calls that send events that are
            // processed through the framework and eventually find their way to
            // affecting the ui thread.
            getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    mRightButton.requestFocus();
                }
            });
            // wait for the request to go through
            getInstrumentation().waitForIdleSync();
    
            assertTrue(mRightButton.isFocused());
    
            sendKeys(KeyEvent.KEYCODE_DPAD_LEFT);
            assertTrue("center button should be focused", mCenterButton.isFocused());
        }
    

     このテストケースでは、イベントキーを送るのではなく、直接rightbuttonにフォーカスをあてますが、その処理をUI用スレッドにて行っています。

            // affecting the ui thread.
            getActivity().runOnUiThread(new Runnable() {
                public void run() {
                    mRightButton.requestFocus();
                }
            });
    

     別スレッドにて操作を行っているため、操作が完了しアイドル状態になるまで、下記メソッドをコールして同期させています。

            // wait for the request to go through
            getInstrumentation().waitForIdleSync();
    

     Activityに対する操作は別スレッドにて行うというのを意識しておかないと、単体テスト時に泥沼にはまりそうですね。

    taga Android , , ,