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.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);
}
}
}
};
}
}