Написание тестов для приложения для Android, которое входит в Facebook: UIAutomator не может заполнить текст в поле имени пользователя Facebook

Из этого вопроса/ответа у меня возникла идея использовать UIAutomator для тестирования моего приложения, требующего входа в Facebook.

Написание тестов для приложения Android, которое входит в Facebook

Я попытался

        UiObject2 editText = mDevice.findObject(By.clazz("android.widget.EditText"));
        editText.setText("[email protected]");

Как и другие вещи, но что бы я ни делал, я не могу заставить его заполнить поле. Я использовал инструмент hierachyviewer, чтобы увидеть, что это EditText. Однако это веб-просмотр, поэтому я не знаю.

Это возможно?

Мой полный класс тестового кода прилагается ниже:

package com.greenrobot.yesorno.test;

import android.os.SystemClock;
import android.support.test.InstrumentationRegistry;
import android.support.test.espresso.NoMatchingViewException;
import android.support.test.espresso.UiController;
import android.support.test.espresso.ViewAction;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.test.suitebuilder.annotation.LargeTest;
import android.view.View;

import com.greenrobot.yesorno.Home;
import com.greenrobot.yesorno.R;
import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;

import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import timber.log.Timber;

import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static org.hamcrest.Matchers.hasToString;
import static org.hamcrest.Matchers.startsWith;


/**
 * Created by andytriboletti on 1/15/16.
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class TestLogin  {

    private UiDevice mDevice;

    private static final String PACKAGE_NAME = "com.greenrobot.yesorno";
    private static final int LAUNCH_TIMEOUT = 5000;

    @Rule
    public ActivityTestRule<Home> mActivityRule = new ActivityTestRule(Home.class);

    public TestLogin() {
        super();
    }

    @Before
    public void initTest() {
        // Initialize UiDevice instance
    
        mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());

//
//        // Start from the home screen
//        mDevice.pressHome();
//
//        // Wait for launcher
    
//        String launcherPackage = mDevice.getCurrentPackageName();
//        mDevice.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);
//
//        // Launch the app
    
//        Context context = InstrumentationRegistry.getContext();
//        Intent intent = context.getPackageManager().getLaunchIntentForPackage(PACKAGE_NAME);
//
//        // Clear out any previous instances
    
//        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
//        context.startActivity(intent);
//
//        // Wait for the app to appear
    
//        mDevice.wait(Until.hasObject(By.pkg(PACKAGE_NAME).depth(0)), LAUNCH_TIMEOUT);
    }

    public void fillInEmail() {
        onView(withId(R.id.authButton)).perform(click());


            //new UiObject(new UiSelector().description("Email or Phone")).setText("[email protected]");
            //new UiObject(new UiSelector().
           // boolean result = new UiObject(new UiSelector().className(android.widget.EditText.class.getName())).setText("[email protected]");
            //Timber.d(result.getText());
            //UiObject2 editText = new UiObject2(new UiSelector(). className("android.widget.EditText").instance(0));
            //UiObject editText = new UiObject(new UiSelector().text("Email or Phone"));
            UiObject2 editText = mDevice.findObject(By.clazz("android.widget.EditText"));
            editText.setText("[email protected]");
            SystemClock.sleep(5000);


    }
    @Test
    public void testLogin() {
        try {
            onView(withId(R.id.welcome)).check(matches(isDisplayed()));
            Timber.d("Logged out");
            //fillInEmail();
        }
        catch(NoMatchingViewException e) {

            onView(withId(R.id.name_age)).check(matches(isDisplayed()));
            Timber.d("Not logged out");
            onView(withId(R.id.slidingmenumain)).perform(actionOpenDrawer());
            //SystemClock.sleep(5000);
            onData(hasToString(startsWith("Logout")))
                    .inAdapterView(withId(android.R.id.list))
                    .perform(click());

            //fillInEmail();

            //openDrawer(R.id.drawer_layout);
            //Espresso.onView(Matchers.allOf(ViewMatchers.withId(R.id.drawerItemNameTextView), ViewMatchers.hasSibling(ViewMatchers.withText(((NavDrawerItem)item).getItemName())))).perform(ViewActions.click());


        }

//        onView(withText("Hello world!")).check(matches(isDisplayed()));

        //onView(withId(R.id.changeTextBt)).perform(click());

    }


    private static ViewAction actionOpenDrawer() {
        return new ViewAction() {
            @Override
            public Matcher<View> getConstraints() {
                return isAssignableFrom(SlidingMenu.class);
            }

            @Override
            public String getDescription() {
                return "open drawer";
            }

            @Override
            public void perform(UiController uiController, View view) {
                ((SlidingMenu) view).showMenu();
            }
        };
    }
    private static ViewAction actionCloseDrawer() {
        return new ViewAction() {
            @Override
            public Matcher<View> getConstraints() {
                return isAssignableFrom(SlidingMenu.class);
            }

            @Override
            public String getDescription() {
                return "close drawer";
            }

            @Override
            public void perform(UiController uiController, View view) {
                //((SlidingMenu) view).close(GravityCompat.START);
            }
        };
    }
}

Изменить: на самом деле я использовал uiautomatorviewer, а не hierachyviewer. Вот скриншот, показывающий, что это редактируемый текст. По крайней мере, по этому инструменту. представление инструмента uiautomator веб-просмотра входа в Facebook


person Andy    schedule 12.02.2016    source источник
comment
Вы говорите, что это EditText, но это также WebView? Это не имеет никакого смысла.   -  person Doug Stevenson    schedule 12.02.2016
comment
uiautomatorviewer говорит, что это EditText. Я прикрепил картинку. Я не знаю.   -  person Andy    schedule 12.02.2016
comment
В javadoc для findObject указано, что он дает вам первый объект, соответствующий критериям. У вас есть селектор, который просто соответствует имени класса, поэтому я предполагаю, что вы получаете другой EditText. Я (пока) не являюсь экспертом по uiautomator, но я хотел бы найти способы уточнить, какой EditText вы хотите получить. (Интересно, что он, кажется, преобразует текстовые поля веб-просмотра в выбираемые объекты - не думал, что он может это сделать.)   -  person Doug Stevenson    schedule 12.02.2016
comment
mDevice.findObject(By.clazz(android.widget.EditText)); возвращает ноль.   -  person Andy    schedule 12.02.2016
comment
Я также пробовал: логический результат = новый UiObject(новый UiSelector().className(android.widget.EditText.class.getName())).setText([email protected]); //результат ложный и не работает.   -  person Andy    schedule 12.02.2016
comment
Я думал, что UiAutomator не может найти элементы, отличные от родных. Итак, я брожу, как вы получили этот дамп с помощью uiautomatorviewer. Я ожидаю, что появятся только элементы макета, которые содержат веб-просмотр, и никакой информации об элементах веб-просмотра. Таким образом, этот веб-просмотр, кажется, имеет собственные элементы внутри. Это по умолчанию, который использует вход в Facebook?   -  person Thanasis Petsas    schedule 22.06.2016
comment
@ThanasisPetsas Да, даже инженер Google, который помог мне, не знал, что это работает для виджетов внутри веб-просмотра, пока он не исследовал и не помог мне решить проблему. Это веб-просмотр по умолчанию, который использует Facebook. Насколько я помню, это не будет работать с эмулятором Genymotion, только со стандартным эмулятором.   -  person Andy    schedule 23.06.2016
comment
@ Энди Интересно .. Я не знал об этом, я проведу несколько тестов. Итак, можно ли взаимодействовать с этими элементами через UIAutomator? Это было бы действительно круто!   -  person Thanasis Petsas    schedule 23.06.2016
comment
Да, именно это делает мой тест.   -  person Andy    schedule 23.06.2016
comment
Да, я попробовал это с uiautomatorview, и это работает. Дело в том, что дамп uiautomator показывает информацию только о макетах и ​​веб-просмотре без его элемента в момент появления веб-просмотра со страницей входа в facebook. Затем, когда я нажимаю на элемент (например, текстовое поле) и делаю новый дамп, все элементы отображаются в средстве просмотра. Звучит немного странно. Надеюсь, это тоже сработает в моих тестах.   -  person Thanasis Petsas    schedule 23.06.2016
comment
Ха-ха, мои тесты uiautomator могут обрабатывать вход в Facebook прямо сейчас! Прохладно! Спасибо @Энди! :)   -  person Thanasis Petsas    schedule 24.06.2016
comment
Не могли бы вы опубликовать свой окончательный код???   -  person GMX    schedule 19.01.2017


Ответы (1)


Я не пробовал это на странице входа в Facebook, но смог сделать это на другой странице, которую я создал.

Я смог использовать как UIObject, так и UIObject2 для размещения поля ввода в веб-форме с помощью класса EditText. Мне удалось установить текст в поле ввода с помощью setText(), но я не смог прочитать его с помощью getText().

Выбор форм ввода HTML требует некоторой осторожности. Единственный способ надежно выбрать конкретный вход — использовать instance(), чтобы найти n-е проиндексированное поле. Это означает, что вам, вероятно, следует ограничить область поиска родительским объектом WebView (иначе какой-то другой EditText на экране может сместить все).

Еще одна вещь, на которую вам нужно обратить внимание, — это время загрузки WebView. Это не произойдет мгновенно, поэтому вам нужно будет подождать некоторое разумное время, чтобы страница загрузилась.

Вот небольшой пример кода, который позволяет мне найти второе поле ввода на HTML-странице и поместить в него текст:

UiObject input = mDevice.findObject(new UiSelector()
    .instance(1)
    .className(EditText.class));
input.setText("text");
person Doug Stevenson    schedule 14.02.2016