Поиск…


Вступление

Фокус этого документа заключается в представлении целей и способов написания пользовательских интерфейсов 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.ResourceCallback callback) - обеспечивает обратный вызов, который вы должны вызывать, когда ваш объект переходит в режим ожидания.

Теперь вы должны создать свою собственную логику и определить, когда ваше приложение простаивает, а когда нет, поскольку это зависит от приложения. Ниже вы найдете простой пример, просто чтобы показать, как он работает. В Интернете есть другие примеры, но конкретная реализация приложения приводит к конкретным реализациям ресурсов бездействия.

ЗАМЕТКИ

  • Были примеры 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);
                    }
                }
            }
        };
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow