如何使用 Espresso 空闲资源进行网络调用 [英] How to use Espresso Idling Resource for network calls

查看:29
本文介绍了如何使用 Espresso 空闲资源进行网络调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 Espresso 来测试我的 UI.当我登录到我的应用程序时,我调用 Parse API(网络调用)来验证用户名和密码.如果一切顺利,用户就会被定向到一个新的活动.我想对此进行测试,但我似乎无法处理空闲资源.

Im trying to use Espresso to test my UI. When i login into my application, i do a call to Parse API (network call) to verify the username and password. If all is well the user gets directed to a new activity. I want to test this, but i cant seems to work with the idle resource thing.

代码:

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

}

我现在得到的例外如下:

The exception i get now is the following:

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

当我运行测试时,输入了用户名和密码,但从未调用执行单击,几秒钟后我收到异常.我应该如何正确实现空闲资源?

When i run the test, the username and password are getting filled in but the perform click is never called and i get the exception after a few seconds. How should i implement the idle resource correctly?

编辑 --

我建议在 Android 上使用 Calabash.Calabash 的工作原理类似,但不需要您更改应用程序代码进行测试.

I would recommend using Calabash for Android. Calabash works similar but doesn't need you to change your app code for testing.

推荐答案

就像其他答案所暗示的那样,countingIdlingResource 并不真正适用于您的用例.

Like the other answer suggests, the countingIdlingResource does not really apply for your use case.

我一直做的是添加一个接口 - 我们称之为 ProgressListener - 作为活动/片段的字段,该字段具有要等待的资源(异步后台工作,更长的网络会话,等)以及每次显示或取消进度时通知它的方法.

What I always do is add an interface - let's call this one ProgressListener - as a field of the activity / fragment that has a resource to be waited on (asynchronous background work, longer networking sessions, etc.) and a method to notify it everytime the progress is shown or dismissed.

我假设您在 LoginActivity 中有您的凭据验证逻辑和对 Parse API 的调用,如果成功,它将调用 MainActivity 的意图.

I'm assuming you have your credentials validation logic and the call to the Parse API in the LoginActivity, which will then call an intent to the MainActivity if successful.

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

Then, simply implement the IdlingResource class and override its methods to communicate when the resource goes from busy to idle through its 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() 方法中注册您的自定义空闲资源:

Last step is to register your custom idling resource in the test's setUp() method:

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

就是这样!现在 espresso 将等待您的登录过程完成,然后继续所有其他测试.

And that's it! Now espresso will wait for your login process to complete and then continue with all the other tests.

如果我不够清楚,或者这正是您需要的,请告诉我.

Please let me know if I wasn't clear enough or if that is exactly what you needed.

这篇关于如何使用 Espresso 空闲资源进行网络调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆