Как использовать ресурс Idluso Idling для сетевых вызовов

Я пытаюсь использовать Espresso для тестирования моего пользовательского интерфейса. Когда я вхожу в систему в своем приложении, я звоню в Parse API (сетевой вызов), чтобы проверить имя пользователя и пароль. Если все хорошо, пользователь переходит к новому действию. Я хочу проверить это, но я не могу работать с файлом бездействия.

Код:

public class ApplicationTest extends ActivityInstrumentationTestCase2<LoginActivity> { private CountingIdlingResource fooServerIdlingResource; public ApplicationTest() { super(LoginActivity.class); } @Before public void setUp() throws Exception { super.setUp(); injectInstrumentation(InstrumentationRegistry.getInstrumentation()); getActivity(); CountingIdlingResource countingResource = new CountingIdlingResource("FooServerCalls"); this.fooServerIdlingResource = countingResource; Espresso.registerIdlingResources(countingResource); } public void testChangeText_sameActivity() { // Type text and then press the button. onView(withId(R.id.username)) .perform(typeText("s@s.nl"), closeSoftKeyboard()); onView(withId(R.id.password)) .perform(typeText("s"), closeSoftKeyboard()); if(performClick()) onView(withId(R.id.main_relative_layout)) .check(matches(isDisplayed())); // Check that the text was changed. } public boolean performClick(){ fooServerIdlingResource.increment(); try { onView(withId(R.id.login)).perform(click()); return true; } finally { fooServerIdlingResource.decrement(); } } @SuppressWarnings("javadoc") public final class CountingIdlingResource implements IdlingResource { private static final String TAG = "CountingIdlingResource"; private final String resourceName; private final AtomicInteger counter = new AtomicInteger(0); private final boolean debugCounting; // written from main thread, read from any thread. private volatile ResourceCallback resourceCallback; // read/written from any thread - used for debugging messages. private volatile long becameBusyAt = 0; private volatile long becameIdleAt = 0; /** * Creates a CountingIdlingResource without debug tracing. * * @param resourceName the resource name this resource should report to Espresso. */ public CountingIdlingResource(String resourceName) { this(resourceName, false); } /** * Creates a CountingIdlingResource. * * @param resourceName the resource name this resource should report to Espresso. * @param debugCounting if true increment & decrement calls will print trace information to logs. */ public CountingIdlingResource(String resourceName, boolean debugCounting) { this.resourceName = checkNotNull(resourceName); this.debugCounting = debugCounting; } @Override public String getName() { return resourceName; } @Override public boolean isIdleNow() { return counter.get() == 0; } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } /** * Increments the count of in-flight transactions to the resource being monitored. * <p/> * This method can be called from any thread. */ public void increment() { int counterVal = counter.getAndIncrement(); if (0 == counterVal) { becameBusyAt = SystemClock.uptimeMillis(); } if (debugCounting) { Log.i(TAG, "Resource: " + resourceName + " in-use-count incremented to: " + (counterVal + 1)); } } /** * Decrements the count of in-flight transactions to the resource being monitored. * <p/> * If this operation results in the counter falling below 0 - an exception is raised. * * @throws IllegalStateException if the counter is below 0. */ public void decrement() { int counterVal = counter.decrementAndGet(); if (counterVal == 0) { // we've gone from non-zero to zero. That means we're idle now! Tell espresso. if (null != resourceCallback) { resourceCallback.onTransitionToIdle(); } becameIdleAt = SystemClock.uptimeMillis(); } if (debugCounting) { if (counterVal == 0) { Log.i(TAG, "Resource: " + resourceName + " went idle! (Time spent not idle: " + (becameIdleAt - becameBusyAt) + ")"); } else { Log.i(TAG, "Resource: " + resourceName + " in-use-count decremented to: " + counterVal); } } checkState(counterVal > -1, "Counter has been corrupted!"); } /** * Prints the current state of this resource to the logcat at info level. */ public void dumpStateToLogs() { StringBuilder message = new StringBuilder("Resource: ") .append(resourceName) .append(" inflight transaction count: ") .append(counter.get()); if (0 == becameBusyAt) { Log.i(TAG, message.append(" and has never been busy!").toString()); } else { message.append(" and was last busy at: ") .append(becameBusyAt); if (0 == becameIdleAt) { Log.w(TAG, message.append(" AND NEVER WENT IDLE!").toString()); } else { message.append(" and last went idle at: ") .append(becameIdleAt); Log.i(TAG, message.toString()); } } } } 

}

Исключением я получаю следующее:

 ndroid.support.test.espresso.IdlingResourceTimeoutException: Wait for [FooServerCalls] to become idle timed out 

Когда я запускаю тест, имя пользователя и пароль заполняются, но выполнение клика никогда не вызывается, и я получаю исключение через несколько секунд. Как я должен правильно реализовать ресурс бездействия?

РЕДАКТИРОВАТЬ —

Я бы рекомендовал использовать Calabash для Android. Calabash работает аналогично, но вам не нужно менять код приложения для тестирования.

Solutions Collecting From Web of "Как использовать ресурс Idluso Idling для сетевых вызовов"

Как и в другом ответе, countingIdlingResource действительно не применяется для вашего случая использования.

То, что я всегда делаю, это добавить интерфейс – давайте назовем это ProgressListener – как поле активности / фрагмента, в котором есть ресурс, на который нужно ждать (асинхронная фоновая работа, более длительные сеансы работы в сети и т. Д.) И способ уведомления об этом каждый раз Прогресс показывается или отклоняется.

Я предполагаю, что у вас есть логика проверки учетных данных и вызов API-интерфейса Parse в LoginActivity , который затем вызовет намерение MainActivity случае успеха.

 public class LoginActivity extends AppCompatActivity { private ProgressListener mListener; ... public interface ProgressListener { public void onProgressShown(); public void onProgressDismissed(); } public void setProgressListener(ProgressListener progressListener) { mListener = progressListener; } ... public void onLoginButtonClicked (View view) { String username = mUsername.getText().toString(); String password = mPassword.getText().toString(); // validate credentials for blanks and so on // show progress and call parse login in background method showProgress(); ParseUser.logInInBackground(username,password, new LogInCallback() { @Override public void done(ParseUser parseUser, ParseException e) { dismissProgress(); if (e == null){ // Success!, continue to MainActivity via intent Intent intent = new Intent (LoginActivity.this, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); } else { // login failed dialog or similar. } } }); } private void showProgress() { // show the progress and notify the listener ... notifyListener(mListener); } private void dismissProgress() { // hide the progress and notify the listener ... notifyListener(mListener); } public boolean isInProgress() { // return true if progress is visible } private void notifyListener(ProgressListener listener) { if (listener == null){ return; } if (isInProgress()){ listener.onProgressShown(); } else { listener.onProgressDismissed(); } } } 

Затем просто реализуйте класс IdlingResource и переопределите его методы для связи, когда ресурс переходит из занятого в режим ожидания через свой ResourceCallBack

 public class ProgressIdlingResource implements IdlingResource { private ResourceCallback resourceCallback; private LoginActivity loginActivity; private LoginActivity.ProgressListener progressListener; public ProgressIdlingResource(LoginActivity activity){ loginActivity = activity; progressListener = new LoginActivity.ProgressListener() { @Override public void onProgressShown() { } @Override public void onProgressDismissed() { if (resourceCallback == null){ return ; } //Called when the resource goes from busy to idle. resourceCallback.onTransitionToIdle(); } }; loginActivity.setProgressListener (progressListener); } @Override public String getName() { return "My idling resource"; } @Override public boolean isIdleNow() { // the resource becomes idle when the progress has been dismissed return !loginActivity.isInProgress(); } @Override public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } } 

Последний шаг – зарегистрировать свой пользовательский ресурс холостого хода в setUp() теста:

 Espresso.registerIdlingResources(new ProgressIdlingResource((LoginActivity) getActivity())); 

Вот и все! Теперь эспрессо будет ждать завершения вашего процесса входа в систему, а затем продолжить все остальные тесты.

Пожалуйста, дайте мне знать, если я не был достаточно ясен, или если это именно то, что вам нужно.

Espresso будет опробовать ресурс холостого хода непосредственно перед выполнением щелчка (или любого действия вида). Но вы не уменьшаете счетчик до тех пор, пока не щелкнете. Это тупик.

Я не вижу быстрого решения здесь; Ваш подход на самом деле не имеет смысла для меня. Приходят в голову несколько возможных альтернативных подходов:

  • В зависимости от того, какую библиотеку вы используете для работы в сети, вы можете написать ресурс холостого хода, который проверяет, выполняется ли звонок.
  • Если во время входа в систему отображается индикатор прогресса, вы можете установить IdlingResource, который ждет его исчезновения.
  • Вы можете дождаться запуска следующего действия или для отображения или исчезновения определенного вида.

Другой подход заключается в том, чтобы иметь собственный ресурс Idling, который может проверять вашу активность. Я создал его здесь:

 public class RequestIdlingResource implements IdlingResource { private ResourceCallback resourceCallback; private boolean isIdle; @Override public String getName() { return RequestIdlingResource.class.getName(); } @Override public boolean isIdleNow() { if (isIdle) return true; Activity activity = getCurrentActivity(); if (activity == null) return false; idlingCheck(activity); if (isIdle) { resourceCallback.onTransitionToIdle(); } return isIdle; } private Activity getCurrentActivity() { final Activity[] activity = new Activity[1]; java.util.Collection<Activity> activities = ActivityLifecycleMonitorRegistry.getInstance().getActivitiesInStage(Stage.RESUMED); activity[0] = Iterables.getOnlyElement(activities); return activity[0]; } @Override public void registerIdleTransitionCallback( ResourceCallback resourceCallback) { this.resourceCallback = resourceCallback; } public void idlingCheck(Activity activity) { /* Look up something (view or method call) on the activity to determine if it is idle or busy */ } } 

https://gist.github.com/clivejefferies/2c8701ef70dd8b30cc3b62a3762acdb7

Я получил вдохновение отсюда, что показывает, как его можно использовать в тесте:

https://github.com/AzimoLabs/ConditionWatcher/blob/master/sample/src/androidTest/java/com/azimolabs/f1sherkk/conditionwatcherexample/IdlingResourceExampleTests.java

Хорошо, что вам не нужно добавлять код проверки в класс реализации.