takaiwa.net

Android、Java、Web系、Linux、マラソン等の備忘録

2013/01/20

JUnitからAlertDialog.Builderのボタンを押す

0 件のコメント
たぶんやり方としておかしいというかスマートじゃない思うんですけど、テストやるのに結果として押せりゃいいんだよ押せりゃ...
ということで、眠いし、深く検証しないままメモしておきます。

テストケースは、ActivityInstrumentationTestCase2を継承したもので、画面にダイアログが表示されている状態で、以下のコードでボタン押下を確認しました。
[PositiveButtonを押下する]
        // PositiveButtonボタン押下
        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);

[NegativeButtonを押下する]
        //NegativeButtonボタン押下
        sendKeys(KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);


テストケースだと一瞬なので、Thread.sleepとか入れると目視でクリックされるのが確認できると思います。

※追記(2013/01/29):ただしダイアログ内にほとんど中身(View)がない場合に限る。中身があって、別のViewをタッチしてしまう場合は、KEYCODE_DPAD_RIGHTやKEYCODE_DPAD_LEFTなど、いくつかのキーを送信すればOKボタンまでフォーカスを移動させることができ、最後にsendKeys(KeyEvent.KEYCODE_DPAD_DOWN, KeyEvent.KEYCODE_DPAD_CENTER);を実行するとOKボタンを押せるみたい

2013/01/11

JUnitからMenuItemをクリックする方法

0 件のコメント
ActivityInstrumentationTestCase2を継承したクラスのテストメソッドで確認しました。

getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_MENU);
getInstrumentation().invokeMenuActionSync(activity, net.takaiwa.R.id.menu_setting, 0);

invokeMenuActionSyncのパラメータは、
第一引数 対象Activity
第二引数 メニューアイテムのID
xmlに定義したもの?
第三引数 なんかフラグ

XMLではなく動的に追加したメニューの場合は、MenuItemをgetDeclaredFieldなり何らかの形で取得して、getItemIdで第二引数のIDを取得できます。以下、
        Class c = mActivity.getClass();
        Field field = c.getDeclaredField("mMenuItemHoge");
        MenuItem menu_item = field.get(mActivity);
        getInstrumentation().invokeMenuActionSync(mActivity, menu_item.getItemId(), 0);

コンパイル通してないですけど、こんな感じですかね。MenuItemはactivityからたどれそうな気もしますけど。

2013/01/09

Javaでprivateなfieldやmethodにアクセスする

0 件のコメント
JUnitでテストしてると、privateなフィールドにアクセスして、値を参照したりセットしたりしたくなるわけですが、よく使うのでメモしておきます。

例えば、次のような対象のクラスがあるとします。
    public class ParentClass {
        private String hoge = "ParentClass!!";

        public void dispMsg() {
            System.out.println("dispMsg:" + hoge);
        }

        private void privateDispMsg(String msg) {
            System.out.println("dispMsg:" + msg);
        }
    }

2013/01/08

onCreate実行前にActivity内メソッドの戻り値を差し替てテストしたい。Mockitoで

0 件のコメント
-------------追記:2015/01/12-------------
書き直しました→Mockitoを使ってActivityの分岐のテスト ~ takaiwa.net
-----------------------------------------
良いやり方ではないと思うんですけど、力業で解決したので載せておきます。
やりたい事はこの質問者さんとだいたい同じだと思います(Activityの話かどうかわかりませんが)
・onCreateメソッドのテスト - Google グループ
https://groups.google.com/forum/#!msg/android-group-japan/0H_wA7IYJ6k/gdqwloJnjR8J
setUp()メソッドをオーバーライドしてgetActivity()を記述しても、その時点でonCreateが呼ばれてしまうんですね。何でonCreate前にそんな事をしたいのかと言うと、上記の質問および下記のサンプルソースから察していただければと思うんですけど、
[テスト対象Activity]
public class MainActivity extends Activity {

    private ServerAPI server_api;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if(true == isLoggedIn()) {
            ((TextView)findViewById(R.id.textView1)).setText("Logged In");
        }
        else {
            ((TextView)findViewById(R.id.textView1)).setText("Not logged in");
        }
    }

    public boolean isLoggedIn() {
        server_api = new ServerAPI();
        // falseが返る
        return server_api.isConnected();
    }
}

isLoggedIn()はサーバへアクセスするメソッドという風に考えてもらって、戻り値がtrueなら、ログイン済みという旨をTextViewへ表示。falseならログイン未という旨を表示するActivityです。テストの度にサーバへぼこぼこアクセスするのも気が引けますし(※1)、単体テストにログイン/ログアウトの手順を踏むのも違う気が。JUnitのテストプロジェクトから自由にisLoggedIn()の戻り値を書き換えてonCreate内の分岐をテストしたいわけです。が、先ほど申しましたようにgetActivity()時点でonCreate()まで実行済みになってしまう。他に良いAPIがあるのでしょうか?

2013/01/07

AndroidのテストをMockitoで

0 件のコメント
以前Android Mockを泣きそうになりながら動かしてみたんですが、@ITの記事を書いた方のブログを見つけました。
・さようならAndroid Mock #android_tec - やらなイカ?
http://nowsprinting.hatenablog.com/entry/2012/11/26/130050
Android Mockは3年前、4週間のプロジェクトとして(より自然なソリューションが出てくるまでの)応急的ソリューションとして開発された....
お蔵入りのようなことが書いてあり、またMockitoがAndroidネイティブサポートを正式にマージしたとのことやネットで感触が良いような記事を目にしたので今度はMockito動かしてみたいと思います。僕のやり方がマズいのかまたもや躓いたので、やり方をメモしておきたいと思います。

[参考]

・MockitoをAndroidテストプロジェクトで使用する #android_tec - やらなイカ?
http://nowsprinting.hatenablog.com/entry/2012/12/01/091916

・Androidでmockitoを使ったJUnitの単体テストを動かす #Java #Android - Qiita
http://qiita.com/items/c695e59c2220670ebe04

[環境]
  • Eclipse:Helios Service Release 2
  • Android Development Tools: 21.0.0.v201210310015-519525
  • OS:Win7
[手順]
  1. Downloads - mockito - simpler & better mocking - Google Project Hosting からmockito-all-1.9.5.jarをダウンロード
  2. Downloads - dexmaker - Programmatic code generation for Android. - Google Project Hostingからdexmaker-mockito-1.0.jarとdexmaker-1.0.jarをダウンロード
  3. テストプロジェクトのlibsフォルダに上記jarファイルをすべて突っ込む
参考サイトにビルドパスの設定について書かれてたりしますが、libsフォルダにまとめて入れるだけでOKのようです。

※”Add JARs..ボタンで追加”とありますが、そもそもそうしようと思ったらlibsフォルダに入ってる前提ということでしょうか?そこを外部jarの追加でOKだと勘違いしてしまってたのが原因でした。ちなみに外部jarで追加するとこのようなエラーが出ます
java.lang.NoClassDefFoundError: org.mockito.Mockito
at com.example.hoge.test.FirstTest.testHoge(FirstTest.java:15)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:169)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:154)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:537)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1551)

確認のソースコードについては、参考サイトに上げてるQiitaのページにあるソースで確認すれば良いと思います。そのソースコードは、ArrayListのtoStringの戻り値を差し替えています。

紹介されているソースで例えば、
        ArrayList hoge = mock(ArrayList.class);
        when(hoge.toString()).thenReturn("hoge");
        assertEquals ( "hoge", hoge.toString() );
        hoge.add("moge");
        assertEquals(0, hoge.size());

のように、hogeに値を追加しても、hoge.size()の戻り値は0で他のメソッドは機能していません。これが戻り値の型がStringなどの場合はnullが返ってきます。toStringだけの戻り値を差し替えたい場合は、spyを使います。

        ArrayList hoge2 = spy(new ArrayList());
        when(hoge2.toString()).thenReturn("hoge");
        assertEquals ( "hoge", hoge2.toString() );
        hoge2.add("moge");
        assertEquals(1, hoge2.size());

のようにすれば、変更されているのはtoStringの戻り値だけです。こんな使い方ができるspyが重宝すると思われます。

追記:2013/06/30 JUnit Testで実行する場合

Android JUnit Testではなく、JUnit Testでロジックテストするなどの場合は、


上記ではJarファイルをlibsフォルダに入れておくだけと記述してますが、Build Pathの設定からAdd JARsボタン押下でmockito-all-1.9.5.jarを追加しておく必要があります。

関連:試行錯誤ログ: Android Test ProjectでJUnit Testが起動しない

PR:JUnit実践入門 ~体系的に学ぶユニットテストの技法 (WEB+DB PRESS plus)
PR:テスト駆動開発入門