Android Espresso パスワードなどのバリデーションチェック

Android

登録フォームなどのテスト、一番いやですよね~
あれやこれやと境界値を入力して、エラーメッセージを確認したり…。
手でやるとしんどいので、こういう時こそEspressoの出番です。

//RegisterActivity.java

 public class Validate {
 
    public static List<String> validateNormalRegisterInfo(HashMap<String, String> info, Context context){
 
        List<String> errorList = new ArrayList<>();
 
        if (! isEnoughCharacters(info.get("password"), 6)) {
            errorList.add(context.getString(R.string.error_password));
        }else if ( !info.get("password").matches("[0-9a-zA-Z]+")) {
            errorList.add(context.getString(R.string.error_password_novalid));
        }
 
        return errorList;
    }
 //ValidateTest.java
 public class  ValidateTest {
 
    @Rule
    public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
 
    @Test
    public void isEnoughCharacter_ForPassword(){
 
        HashMap<String, String> map = new HashMap();
        map.put("password", "12345");
 
        List<String> testErrorList = new ArrayList<>();
 
        Activity activity = mActivityRule.getActivity();
        testErrorList.add(activity.getString(R.string.error_password));
        
        Assert.assertEquals(testErrorList, Validate.validateNormalRegisterInfo(map, activity));
    }
 
    @Test
    public void isValidCharacter_ForPassword(){
 
        HashMap<String, String> map = new HashMap();
        map.put("password", "うりぃぃぃぃぃぃ");
 
        List<String> testErrorList = new ArrayList<>();
 
        Activity activity = mActivityRule.getActivity();
        testErrorList.add(activity.getString(R.string.error_password_novalid));
 
        Assert.assertEquals(testErrorList, Validate.validateNormalRegisterInfo(map, activity));
    }
 
 }

あまり本題ではないのですが、テストコードからリソースを読みだすところ

 getString(R.string.error_password)

に地味に詰まりましたね。

まだまだEspresso、勉強中なので、こうすればいいよ!とかあれば、教えてください。┌o ペコッ

Android Espresso scrollToが効かない

Android
Espresso

Espressoの面倒なところが、UIで見えてないボタンなどについて次のようにテストしようとすると、

 onView(withId(R.id.face_photo_upload)).perform(scrollTo()).perform(click());
 android.support.test.espresso.PerformException: Error performing 'Send down motion event' on view 'unknown'.

とか

 is not displayed

とかでFailureになっちゃうことですね。

ソフトキーボードが開いていると、scrollToを指定しても、うまくスクロールされません。

なので、最初にソフトキーボードが開いちゃうような画面では、ソフトキーボードを閉じてから、テストするとうまくいきます。

 onView(withId(R.id.last_name)).perform(closeSoftKeyboard());   
 onView(withId(R.id.face_photo_upload)).perform(scrollTo()).perform(click());

Android Espresso cannot resolve intending

Android
Espresso

Espressoで、例えばあるアクティビティから、画像ファイルを取得して、戻る、というメソッドのテストをします。

Espresso 初心者用導入のチュートリアルURLで紹介したように、下記のURLによいサンプルがあるので、これを見て、やってみるぞ!となります。

https://codelabs.developers.google.com/codelabs/android-testing/index.html?index=..%2F..%2Findex&hl=ja#7

しかし、肝心の

 intending(hasAction(MediaStore.ACTION_IMAGE_CAPTURE)).respondWith(result);

で、cannot resolve intending でintendingが見つかりません、となっちゃいます。

最初は、サンプルにあるように、AndroidTestMockとかに入れないといけないのかな?と思ってましたが、違いました。

下記にあるように、Espresso IntentsはEspressoの拡張なので、
https://google.github.io/android-testing-support-library/docs/espresso/intents/
appのbuild.gradleに、次の行を足します。

 androidTestCompile 'com.android.support.test.espresso:espresso-intents:2.2.2'

これで、無事解決です!

Android Espresso AsyncでファイルをアップロードするAPIのテスト

Android

Android Espresso パスワードなどのバリデーションチェックなどでも書きましたが、登録フォームのテスト!これは時間がかかるし、嫌なものですよね~(;´Д`)

こういうことこそ、Espressoの出番です!
やることは、Androidアプリから、サーバーに登録情報をPostします。
ファイルを2つアップロード、Emailの重複は許可されていないので、Emailはランダムな文字列を作ります。
APIでEmailが重複していないので登録成功、Emailが重複しているので登録エラー、の2つの場合の返り値を読み取ってテストするところまでです。

CountDownLatchを使ってやってみましたが、もっとスマートな方法がありそうなんですけどね~。
毎回言っていますが、どなたか教えてください┌o ペコッ

 public class RegisterAsyncTest extends ActivityInstrumentationTestCase2 {
 
    String mJsonString = null;
    Exception mError = null;
    CountDownLatch signal = null;
    private Activity mActivity;
 
    public RegisterAsyncTest() {
        super(RegisterActivity.class);
    }
 
    @Override
    protected void setUp() throws Exception {
        signal = new CountDownLatch(1);
        mActivity = getActivity();
    }
 
    @Override
    protected void tearDown() throws Exception {
        signal.countDown();
    }
 
    public void testUploadTask() throws InterruptedException {
 
        HashMap<String, String> register_info = new HashMap<>();
 
        register_info.put("last_name", Constants.LAST_NAME);
        register_info.put("first_name", Constants.FIRST_NAME);
        register_info.put("addresses[0][post_code]", Constants.POST_CODE);
        register_info.put("addresses[0][prefecture]", Constants.PREFECTURE);
        register_info.put("addresses[0][city]", Constants.CITY);
        register_info.put("addresses[0][ward]", Constants.WARD);
        register_info.put("addresses[0][town]", Constants.TOWN);
        register_info.put("addresses[0][address]", Constants.ADDRESS);
        register_info.put("addresses[0][tel]", Constants.TEL);
        register_info.put("password", Constants.PASS);
 
        String email = makeRundomMailAddress();
        register_info.put("email", email);
        register_info.put("username",email);
 
        register_info.put("experience_years_from", Constants.EXPERIENCE);
        register_info.put("regular_login", Constants.REGULAR_LOGIN);
 
        register_info.put("licensePhotoFilepath", Constants.LICENCE_PHOTO_PATH);
        register_info.put("facePhotoFilePath", Constants.FACE_PHOTO_PATH);
 
        Context context = mActivity.getApplicationContext();
 
        UploadAsyncTask task = new UploadAsyncTask(context);
        task.setListener(new UploadAsyncTask.UploadTaskListener() {
            @Override
            public void onComplete(String jsonString, Exception e) {
                mJsonString = jsonString;
                mError = e;
                signal.countDown();
            }
        }).execute(register_info);
        signal.await();
 
        assertNull(mError);
        assertFalse(TextUtils.isEmpty(mJsonString));
 
        assertTrue(mJsonString.startsWith("{    \"response\": {        \"status\": \"SUCCESS\""));
        assertTrue(mJsonString.endsWith("}"));
 
    }
 
    /**
     * すでにあるメールアドレスではエラーが帰ってくる
     * @throws InterruptedException
     */
    public void testUploadTaskEmailInvalidFail() throws InterruptedException {
 
        HashMap<String, String> register_info = new HashMap<>();
 
 
        register_info.put("last_name", Constants.LAST_NAME);
        register_info.put("first_name", Constants.FIRST_NAME);
        register_info.put("addresses[0][post_code]", Constants.POST_CODE);
        register_info.put("addresses[0][prefecture]", Constants.PREFECTURE);
        register_info.put("addresses[0][city]", Constants.CITY);
        register_info.put("addresses[0][ward]", Constants.WARD);
        register_info.put("addresses[0][town]", Constants.TOWN);
        register_info.put("addresses[0][address]", Constants.ADDRESS);
        register_info.put("addresses[0][tel]", Constants.TEL);
        register_info.put("password", Constants.PASS);
 
        String email = "hogeo@onlineconsultant.jp";
        register_info.put("email", email);
        register_info.put("username",email);
 
        register_info.put("experience_years_from", Constants.EXPERIENCE);
        register_info.put("regular_login", Constants.REGULAR_LOGIN);
 
        register_info.put("licensePhotoFilepath", Constants.LICENCE_PHOTO_PATH);
        register_info.put("facePhotoFilePath", Constants.FACE_PHOTO_PATH);
 
        Context context = mActivity.getApplicationContext();
 
        UploadAsyncTask task = new UploadAsyncTask(context);
        task.setListener(new UploadAsyncTask.UploadTaskListener() {
            @Override
            public void onComplete(String jsonString, Exception e) {
                mJsonString = jsonString;
                mError = e;
                signal.countDown();
            }
        }).execute(register_info);
        signal.await();
 
        assertNull(mError);
        assertFalse(TextUtils.isEmpty(mJsonString));
 
        assertTrue(mJsonString.startsWith("{    \"response\": {        \"info\": \"email:"));
        assertTrue(mJsonString.endsWith("}"));
 
    }
 
 }

Android Error inflating class com.google.android.maps.MapView

Android

Android Error inflating class com.google.android.maps.MapView

Android のMapviewが表示されないというこのエラー。
検索してみるとAndroidManifest.xmlで

 <uses-library android:name="com.google.android.maps"/>

を宣言していない場合、あとActivityをMapActivityを継承して作っていないことが原因、とありますが、その二つともやってあるのになんで?とだいぶ試行錯誤してしまいました。

原因は、

 super.onCreate(savedInstanceState);

がOnCreate内の後の方で宣言されていたことでした。(^_^;