2015-06-09 13 views
12

Tôi đang cố gắng sử dụng Espresso để kiểm tra giao diện người dùng của mình. Khi tôi đăng nhập vào ứng dụng của mình, tôi thực hiện cuộc gọi đến Parse API (cuộc gọi mạng) để xác minh tên người dùng và mật khẩu. Nếu tất cả là tốt, người dùng sẽ được chuyển hướng đến một hoạt động mới. Tôi muốn thử nghiệm điều này, nhưng tôi không thể dường như làm việc với các nguồn tài nguyên nhàn rỗi.Cách sử dụng Espresso Idling Resource cho các cuộc gọi mạng

Code:

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("[email protected]"), 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()); 
      } 
     } 
    } 
} 

}

Ngoại lệ tôi có được bây giờ là như sau:

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

Khi tôi chạy thử nghiệm, tên người dùng và mật khẩu có được lấp đầy trong nhưng thực hiện nhấp chuột không bao giờ được gọi và tôi nhận được ngoại lệ sau một vài giây. Làm thế nào tôi nên thực hiện các nguồn tài nguyên nhàn rỗi một cách chính xác?

EDIT -

Tôi khuyên bạn nên sử dụng Calabash cho Android. Calabash hoạt động tương tự nhưng không cần bạn thay đổi mã ứng dụng của bạn để thử nghiệm.

Trả lời

16

Giống như câu trả lời khác cho thấy, countIdlingResource không thực sự áp dụng cho trường hợp sử dụng của bạn.

Điều tôi luôn làm là thêm giao diện - hãy gọi đây là ProgressListener - dưới dạng trường hoạt động/đoạn có tài nguyên được chờ đợi (công việc nền không đồng bộ, phiên nối mạng dài hơn, v.v.) và phương pháp để thông báo cho nó mọi lúc, tiến trình được hiển thị hoặc loại bỏ.

Tôi giả sử bạn có logic xác thực thông tin đăng nhập và cuộc gọi tới API phân tích cú pháp trong số LoginActivity, sau đó sẽ gọi đến mục đích MainActivity nếu thành công.

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

Sau đó, bạn chỉ cần thực hiện các lớp IdlingResource và ghi đè phương thức của nó để giao tiếp khi tài nguyên đi từ bận rộn để idle qua 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; 
    } 
} 

Bước cuối cùng của nó là để đăng ký tài nguyên idling tùy chỉnh của bạn trong các thử nghiệm của setUp() phương pháp:

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

Và đó là nó! Bây giờ espresso sẽ đợi quá trình đăng nhập của bạn hoàn thành và sau đó tiếp tục với tất cả các bài kiểm tra khác.

Vui lòng cho tôi biết nếu tôi không đủ rõ ràng hoặc nếu đó chính xác là những gì bạn cần.

+0

Tôi sẽ dùng thử tối nay !! cảm ơn ! sẽ lấy lại cho bạn :) –

+0

Chúc may mắn, Nó có vẻ giống như một chút công việc phụ, nhưng đăng nhập là cực kỳ hữu ích cho tôi – appoll

+0

Tôi đã thử nó và nó đã làm việc !! tôi đã được tắt với mã của tôi! không mong muốn triển khai bất kỳ mã nào trong mã "ứng dụng" của tôi. Đó là một nhược điểm thực sự để thay đổi mã ứng dụng của tôi để làm cho các bài kiểm tra của tôi hoạt động. Tôi nhận được rất nhiều cuộc gọi API Parse. Tôi tin rằng mã của tôi sẽ là một mớ hỗn độn với tất cả điều này! Nhưng đó không phải là câu hỏi, bạn đã trả lời câu hỏi thực sự của tôi. Vì vậy, cảm ơn bạn vì điều đó ! –

0

Espresso sẽ thăm dò ý kiến ​​tài nguyên không hoạt động ngay trước khi thực hiện nhấp chuột (hoặc bất kỳ hành động xem nào). Nhưng bạn không giảm số lượt truy cập của mình cho đến khi sau khi nhấp chuột. Đó là một bế tắc.

Tôi không thấy bất kỳ sửa chữa nhanh nào ở đây; cách tiếp cận của bạn không thực sự hợp lý với tôi. Một vài phương pháp thay thế có thể xảy ra cho tâm trí:

  • Tùy thuộc vào thư viện bạn sử dụng để kết nối mạng, bạn có thể ghi tài nguyên không hoạt động để kiểm tra xem có đang thực hiện cuộc gọi hay không.
  • Nếu bạn hiển thị chỉ báo tiến trình trong khi đang thực hiện cuộc gọi đăng nhập, bạn có thể cài đặt IdlingResource đang chờ quá trình này biến mất.
  • Bạn có thể đợi cho hoạt động tiếp theo được khởi chạy hoặc để một chế độ xem nhất định xuất hiện/biến mất.
1

Cách tiếp cận khác là có tài nguyên Idling tùy chỉnh có thể kiểm tra hoạt động của bạn. Tôi đã tạo ra một ở đây:

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

tôi đã là nguồn cảm hứng từ đây, trong đó cho thấy làm thế nào nó có thể được sử dụng trong một thử nghiệm:

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

Điều tốt là bạn không phải thêm bất kỳ mã thử nghiệm nào vào lớp triển khai của mình.

Các vấn đề liên quan