Android                
            Написание UI-тестов - Android
        
        
            
    Поиск…
Вступление
Фокус этого документа заключается в представлении целей и способов написания пользовательских интерфейсов Android и интеграционных тестов. Эспрессо и UIAutomator предоставляются Google, поэтому основное внимание должно быть уделено этим инструментам и их соответствующим упаковкам, например, Appium, Spoon и т. Д.
Синтаксис
- Ресурс холостого хода
 - String getName () - возвращает имя ресурса холостого хода (используется для ведения журнала и идемпотентности регистрации).
 - boolean isIdleNow () - возвращает true, если ресурс в настоящее время не используется.
 - void registerIdleTransitionCallback (IdlingResource.ResourceCallback callback) - Регистрирует данный IdlingResource.ResourceCallback с ресурсом
 
замечания
Правила JUnit:
Как вы можете видеть в примере MockWebServer и ActivityTestRule, все они относятся к категории правил JUnit, которые вы можете создать самостоятельно, которые затем должны быть выполнены для каждого теста, определяющего его поведение @see: https://github.com/junit-team/junit4/ вики / правила
Appium
параметры
Поскольку параметры имеют некоторые проблемы, помещая их здесь, пока не будет устранена ошибка в документации:
| параметр | подробности | 
|---|---|
| Класс activityClass | какую деятельность начать | 
| initialTouchMode | если при запуске деятельность должна быть помещена в режим касания: https://android-developers.blogspot.de/2008/12/touch-mode.html | 
| launchActivity | true, если действие должно быть запущено один раз для каждого метода тестирования. Он будет запущен до первого метода Before и завершен после последнего метода After. | 
Пример MockWebServer
В случае, если ваши действия, фрагменты и пользовательский интерфейс требуют некоторой фоновой обработки, полезно использовать MockWebServer, который работает локально на устройстве Android, что обеспечивает закрытую и проверяемую среду для вашего пользовательского интерфейса.
https://github.com/square/okhttp/tree/master/mockwebserver
Первый шаг включает в себя зависимость градиента:
testCompile 'com.squareup.okhttp3:mockwebserver:(insert latest version)'
 Теперь шаги для запуска и использования макетного сервера:
- создать макет серверного объекта
 - запустите его по определенному адресу и порту (обычно localhost: portnumber)
 - выдавать ответы для конкретных запросов
 - начать тест
 
Это хорошо объяснено на странице github mockwebserver, но в нашем случае мы хотим, чтобы что-то было более приятным и многоразовым для всех тестов, и правила JUnit пойдут в игру:
/**
 *JUnit  rule that starts and stops a mock web server for test runner
*/
 public class MockServerRule extends UiThreadTestRule {
 private MockWebServer mServer;
 public static final int MOCK_WEBSERVER_PORT = 8000;
    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                startServer();
                try {
                    base.evaluate();
                } finally {
                    stopServer();
                }
            }
        };
    }
    /**
     * Returns the started web server instance
     *
     * @return mock server
     */
    public MockWebServer server() {
        return mServer;
    }
    public void startServer() throws IOException, NoSuchAlgorithmException {
        mServer = new MockWebServer();
        try {
            mServer(MOCK_WEBSERVER_PORT);
        } catch (IOException e) {
            throw new IllegalStateException(e,"mock server start issue");
        }
    }
    public void stopServer() {
        try {
            mServer.shutdown();
        } catch (IOException e) {
            Timber.e(e, "mock server shutdown error”);
        }
    }
}
 Теперь давайте предположим, что мы имеем ту же самую активность, что и в предыдущем примере, только в этом случае, когда мы нажимаем кнопку, приложение будет извлекать что-то из сети, например: https://someapi.com/name
Это вернет некоторую текстовую строку, которая будет конкатенирована в тексте закутки, например, NAME + текст, который вы ввели.
/**
* Testing of the snackbar activity with networking.
**/
@RunWith(AndroidJUnit4.class)
@LargeTest
public class SnackbarActivityTest{
    //espresso rule which tells which activity to start
    @Rule
    public final ActivityTestRule<SnackbarActivity> mActivityRule = 
        new ActivityTestRule<>(SnackbarActivity.class, true, false);
    //start mock web server
    @Rule
    public final MockServerRule mMockServerRule = new MockServerRule();
    @Override
    public void tearDown() throws Exception {
       //same as previous example
    }
    
    @Override
    public void setUp() throws Exception {
       //same as previous example
       **//IMPORTANT:** point your application to your mockwebserver endpoint e.g.
       MyAppConfig.setEndpointURL("http://localhost:8000");
    }
    
    /**
    *Test methods should always start with "testXYZ" and it is a good idea to 
    *name them after the intent what you want to test
    **/
    @Test
    public void testSnackbarIsShown() {
        //setup mockweb server
        mMockServerRule.server().setDispatcher(getDispatcher());
        mActivityRule.launchActivity(null);
        //check is our text entry displayed and enter some text to it
        String textToType="new snackbar text";
        onView(withId(R.id.textEntry)).check(matches(isDisplayed()));
        //we check is our snackbar showing text from mock webserver plus the one we typed
        onView(withId(R.id.textEntry)).perform(typeText("JazzJackTheRabbit" + textToType));
        //click the button to show the snackbar
        onView(withId(R.id.shownSnackbarBtn)).perform(click());
        //assert that a view with snackbar_id with text which we typed and is displayed
        onView(allOf(withId(android.support.design.R.id.snackbar_text), 
        withText(textToType))) .check(matches(isDisplayed()));
    }
    
     /**
     *creates a mock web server dispatcher with prerecorded requests and responses
     **/
    private Dispatcher getDispatcher() {
        final Dispatcher dispatcher = new Dispatcher() {
            @Override
            public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
                if (request.getPath().equals("/name")){
                    return new MockResponse().setResponseCode(200)
                            .setBody("JazzJackTheRabbit");
                }
                throw new IllegalStateException("no mock set up for " + request.getPath());
            }
        };
        return dispatcher;
    }
 Я бы предложил обернуть диспетчера в какой-то Builder, чтобы вы могли легко связать и добавить новые ответы для своих экранов. например
 return newDispatcherBuilder()
            .withSerializedJSONBody("/authenticate", Mocks.getAuthenticationResponse())
            .withSerializedJSONBody("/getUserInfo", Mocks.getUserInfo())
            .withSerializedJSONBody("/checkNotBot", Mocks.checkNotBot());
        IdlingResource
 Сила ресурсов холостого хода заключается в том, что вам не нужно ждать обработки некоторых приложений (сетей, вычислений, анимаций и т. Д.), Чтобы закончить sleep() , что приводит к отслаиванию и / или продлению тестов. Официальную документацию можно найти здесь . 
Реализация
 Для реализации интерфейса IdlingResource необходимо выполнить IdlingResource : 
-  
getName()- возвращает имя вашего ресурса холостого хода. -  
isIdleNow()- проверяет, является ли ваш объект xyz, операция и т. д. в настоящий момент бездействующим. -  
registerIdleTransitionCallback(IdlingResource.ResourceCallbackcallback) - обеспечивает обратный вызов, который вы должны вызывать, когда ваш объект переходит в режим ожидания. 
Теперь вы должны создать свою собственную логику и определить, когда ваше приложение простаивает, а когда нет, поскольку это зависит от приложения. Ниже вы найдете простой пример, просто чтобы показать, как он работает. В Интернете есть другие примеры, но конкретная реализация приложения приводит к конкретным реализациям ресурсов бездействия.
ЗАМЕТКИ
-  Были примеры Google, где они помещали 
IdlingResourcesв код приложения. Не делайте этого. Предположительно, они разместили его там, чтобы показать, как они работают. - Сохранение вашего кода в чистоте и соблюдение единого принципа ответственности зависит от вас!
 
пример
Скажем, у вас есть активность, которая делает странные вещи и занимает много времени для загрузки фрагмента, и, таким образом, ваши тесты Espresso терпят неудачу, не имея возможности найти ресурсы из вашего фрагмента (вы должны изменить, как создается ваша деятельность и когда чтобы ускорить его). Но в любом случае, чтобы это было просто, следующий пример показывает, как это должно выглядеть.
Наш пример ресурса холостого хода получит два объекта:
- Тег фрагмента, который вам нужно найти и ждать, чтобы привязаться к этой деятельности.
 - Объект FragmentManager, который используется для поиска фрагмента.
 
/**
 * FragmentIdlingResource - idling resource which waits while Fragment has not been loaded.
 */
public class FragmentIdlingResource implements IdlingResource {
    private final FragmentManager mFragmentManager;
    private final String mTag;
    //resource callback you use when your activity transitions to idle
    private volatile ResourceCallback resourceCallback;
    public FragmentIdlingResource(FragmentManager fragmentManager, String tag) {
        mFragmentManager = fragmentManager;
        mTag = tag;
    }
    @Override
    public String getName() {
        return FragmentIdlingResource.class.getName() + ":" + mTag;
    }
    @Override
    public boolean isIdleNow() {
        //simple check, if your fragment is added, then your app has became idle
        boolean idle = (mFragmentManager.findFragmentByTag(mTag) != null);
        if (idle) {
            //IMPORTANT: make sure you call onTransitionToIdle
            resourceCallback.onTransitionToIdle();
        }
        return idle;
    }
    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}
  Теперь, когда вы создали свой IdlingResource , вам нужно использовать его где-то в порядке? 
использование
Давайте пропустим всю установку тестового класса и посмотрим, как будет выглядеть тестовый пример:
@Test
public void testSomeFragmentText() {
    mActivityTestRule.launchActivity(null);
   
    //creating the idling resource
    IdlingResource fragmentLoadedIdlingResource = new FragmentIdlingResource(mActivityTestRule.getActivity().getSupportFragmentManager(), SomeFragmentText.TAG);
    //registering the idling resource so espresso waits for it
    Espresso.registerIdlingResources(idlingResource1);
    onView(withId(R.id.txtHelloWorld)).check(matches(withText(helloWorldText)));
    //lets cleanup after ourselves
    Espresso.unregisterIdlingResources(fragmentLoadedIdlingResource);
}
 Комбинация с правилом JUnit
Это не сложно; вы также можете применить ресурс холостого хода в форме тестового правила JUnit. Например, скажем, что у вас есть SDK, в котором есть Volley, и вы хотите, чтобы Espresso подождал его. Вместо того, чтобы проходить через каждый тестовый пример или применять его в настройке, вы можете создать правило JUnit и просто написать:
@Rule
public final SDKIdlingRule mSdkIdlingRule = new SDKIdlingRule(SDKInstanceHolder.getInstance());
 Теперь, поскольку это пример, не считайте это само собой разумеющимся; весь код здесь мнимый и используется только в демонстрационных целях:
public class SDKIdlingRule implements TestRule {
    //idling resource you wrote to check is volley idle or not
    private VolleyIdlingResource mVolleyIdlingResource;
    //request queue that you need from volley to give it to idling resource
    private RequestQueue mRequestQueue;
    //when using the rule extract the request queue from your SDK
    public SDKIdlingRule(SDKClass sdkClass) {
        mRequestQueue = getVolleyRequestQueue(sdkClass);
    }
    private RequestQueue getVolleyRequestQueue(SDKClass sdkClass) {
        return sdkClass.getVolleyRequestQueue();
    }
    @Override
    public Statement apply(final Statement base, Description description) {
        return new Statement() {
            @Override
            public void evaluate() throws Throwable {
                //registering idling resource
                mVolleyIdlingResource = new VolleyIdlingResource(mRequestQueue);
                Espresso.registerIdlingResources(mVolleyIdlingResource);
                try {
                    base.evaluate();
                } finally {
                    if (mVolleyIdlingResource != null) {
                        //deregister the resource when test finishes
                        Espresso.unregisterIdlingResources(mVolleyIdlingResource);
                    }
                }
            }
        };
    }
}