Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何在网络通话中使用Espresso空闲资源_Android_Networking_Android Espresso_Idle Processing - Fatal编程技术网

Android 如何在网络通话中使用Espresso空闲资源

Android 如何在网络通话中使用Espresso空闲资源,android,networking,android-espresso,idle-processing,Android,Networking,Android Espresso,Idle Processing,我试着用浓缩咖啡来测试我的用户界面。当我登录到我的应用程序时,我调用parseAPI(网络调用)来验证用户名和密码。如果一切顺利,用户将被引导到一个新的活动。我想测试一下,但我似乎无法处理空闲资源的问题 代码: public class ApplicationTest extends ActivityInstrumentationTestCase2<LoginActivity> { private CountingIdlingResource fooServerIdlingRes

我试着用浓缩咖啡来测试我的用户界面。当我登录到我的应用程序时,我调用parseAPI(网络调用)来验证用户名和密码。如果一切顺利,用户将被引导到一个新的活动。我想测试一下,但我似乎无法处理空闲资源的问题

代码:

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());
            }
        }
    }
}
当我运行测试时,用户名和密码会被填写,但是执行点击从未被调用,几秒钟后我会得到异常。我应该如何正确地实现空闲资源

编辑--


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

Espresso将在执行单击(或任何查看操作)之前轮询空闲资源。但是,在单击之后,您才减少计数器。这是一个僵局

我看不到任何快速解决办法;你的方法对我来说毫无意义。我想到了几种可能的替代方法:

  • 根据您用于联网的库的不同,您可能可以编写一个空闲资源来检查是否有正在进行的调用
  • 如果在登录调用进行时显示进度指示器,则可以安装IdlingResource,等待该指示器消失
  • 您可以等待下一个活动启动,或者等待某个视图出现/消失

正如另一个答案所建议的,countingIdlingResource并不真正适用于您的用例

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

我假设您有凭证验证逻辑和对
LoginActivity
中的解析API的调用,如果成功,它将调用对
main活动的意图

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();
        }
    }
}
然后,简单地实现该类并重写其方法,以便在资源通过其

最后一步是在测试的
setUp()
方法中注册自定义空闲资源:

Espresso.registerIdlingResources(new ProgressIdlingResource((LoginActivity) getActivity()));
就这样!现在,espresso将等待您的登录过程完成,然后继续进行所有其他测试


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

另一种方法是使用自定义的空闲资源来检查您的活动。我在这里创建了一个:

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

         */
    }
}
公共类RequestIdlingResource实现IdlingResource{
私有资源回调;
私有布尔isIdle;
@凌驾
公共字符串getName(){
返回RequestIdlingResource.class.getName();
}
@凌驾
公共布尔值isIdleNow(){
if(isIdle)返回true;
活动活动=getCurrentActivity();
if(activity==null)返回false;
空转检查(活动);
如果(isIdle){
resourceCallback.onTransitionToIdle();
}
返回isIdle;
}
私有活动getCurrentActivity(){
最终活动[]活动=新活动[1];
java.util.Collection

我从这里得到了灵感,它展示了如何在测试中使用它:


好的是,您不必向实现类中添加任何测试代码。

上述答案对于2020年来说似乎有些过时。 现在不需要自己创建CountingIdlingResource。已经有了一个。您可以创建它的单例实例,并在活动代码中访问它:

// CountingIdlingResourceSingleton.kt:
import androidx.test.espresso.idling.CountingIdlingResource

object CountingIdlingResourceSingleton {

    private const val RESOURCE = "GLOBAL"

    @JvmField val countingIdlingResource = CountingIdlingResource(RESOURCE)

    fun increment() {
        countingIdlingResource.increment()
    }

    fun decrement() {
        if (!countingIdlingResource.isIdleNow) {
            countingIdlingResource.decrement()
        }
    }
}
然后在应用程序代码中这样使用它:

// MainActivity.kt:
start_activity_button.setOnClickListener {
    val intent = Intent(context, LoginActivity::class.java)

    CountingIdlingResourceSingleton.increment()
    // I am using a kotlin coroutine to simulate a 3 second network request:
    val job = GlobalScope.launch {
        // our network call starts
        delay(3000)
    }
    job.invokeOnCompletion {
        // our network call ended!
        CountingIdlingResourceSingleton.decrement()
        startActivity(intent)
    }
}
然后在测试中注册空闲资源:

// LoginTest.kt: 
@Before
fun registerIdlingResource() {
    IdlingRegistry.getInstance().register(CountingIdlingResourceSingleton.countingIdlingResource)
}

@After
fun unregisterIdlingResource() {
    IdlingRegistry.getInstance().unregister(CountingIdlingResourceSingleton.countingIdlingResource)
}

你可以在我的博客帖子上找到更多关于

我今晚会试试这个!!谢谢!会给你回复:)祝你好运,这看起来可能是一点额外的工作,但是日志记录对我来说非常有帮助。我尝试了它,它成功了!!我的代码太离谱了!我的“应用”中没有实现任何代码代码。更改我的应用程序代码以使我的测试工作正常,这是一个真正的缺点。我有很多解析API调用。我相信我的代码会把这一切弄得一团糟!但这不是问题,你已经回答了我真正的问题。因此,谢谢你!我很高兴这有帮助:)也许这不是唯一的方法,但嘿,不是每个人都编写ui测试和处理同时,它也在联网。所以我认为这是值得的。最好的!您可以通过查找进度视图/片段的可见性而不是添加
isInProgress()来做类似的事情
仅供您使用测试代码。这太棒了,谢谢!我如何等待真正的网络调用而不是模拟的3秒延迟?我希望在网络调用完成后获得一个回调,该回调也在一个协程中运行。您如何在测试中使用它?registerIdleTransitionCallback(IdlingResource.ResourceCallback ResourceCallback),您可以得到它。我们是否需要在生产环境中添加代码来让测试等待它?
// MainActivity.kt:
start_activity_button.setOnClickListener {
    val intent = Intent(context, LoginActivity::class.java)

    CountingIdlingResourceSingleton.increment()
    // I am using a kotlin coroutine to simulate a 3 second network request:
    val job = GlobalScope.launch {
        // our network call starts
        delay(3000)
    }
    job.invokeOnCompletion {
        // our network call ended!
        CountingIdlingResourceSingleton.decrement()
        startActivity(intent)
    }
}
// LoginTest.kt: 
@Before
fun registerIdlingResource() {
    IdlingRegistry.getInstance().register(CountingIdlingResourceSingleton.countingIdlingResource)
}

@After
fun unregisterIdlingResource() {
    IdlingRegistry.getInstance().unregister(CountingIdlingResourceSingleton.countingIdlingResource)
}