2016-07-12 64 views
0

我檢查是否使用濃咖啡顯示吐司時出現問題。我使用的是類:意式濃縮咖啡檢查是否顯示麪包(一個在另一個之上)

 import android.os.IBinder; 
    import android.support.test.espresso.Root; 
    import android.view.WindowManager; 
    import org.hamcrest.Description; 
    import org.hamcrest.TypeSafeMatcher; 

    public class ToastMatcher extends TypeSafeMatcher<Root> { 

    @Override 
    public void describeTo(Description description) { 
     description.appendText("is toast"); 
    } 

    @Override 
    public boolean matchesSafely(Root root) { 
     int type = root.getWindowLayoutParams().get().type; 
     if ((type == WindowManager.LayoutParams.TYPE_TOAST)) { 
      IBinder windowToken = root.getDecorView().getWindowToken(); 
      IBinder appToken = root.getDecorView().getApplicationWindowToken(); 
      if (windowToken == appToken) { 
       // windowToken == appToken means this window isn't contained by any other windows. 
       // if it was a window for an activity, it would have TYPE_BASE_APPLICATION. 
       return true; 
      } 
     } 
     return false; 
    } 

} 

,並通過檢查吐司:

onView(withText(R.string.unauthorized)).inRoot(new ToastMatcher()) 
      .check(matches(isDisplayed())); 

一切工作正常,直到我試圖檢查另一個土司在同一類,例如:

@Test 
public void messageOnBack() throws Exception{ 
pressBack(); 
onView(withText(R.string.exit_on_back)).inRoot(new ToastMatcher()) 
      .check(matches(isDisplayed())); 

然後第一個通過,但第二個存在錯誤:

android.support.test.espresso.NoMatchingViewException: No views in hierarchy found matching: with string from resource id: <2131165323>[unauthorized] value: Wrong login or password. 

View Hierarchy: 
+>LinearLayout{id=-1, visibility=VISIBLE, width=660, height=116, has-focus=false, has-focusable=false, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=0.0, child-count=1} 
| 
+->AppCompatTextView{id=16908299, res-name=message, visibility=VISIBLE, width=528, height=58, has-focus=false, has-focusable=false, has-window-focus=false, is-clickable=false, is-enabled=true, is-focused=false, is-focusable=false, is-layout-requested=false, is-selected=false, root-is-layout-requested=false, has-input-connection=false, x=66.0, y=29.0, text=Please click BACK again to exit., input-type=0, ime-target=false, has-links=false} 
| 
at dalvik.system.VMStack.getThreadStackTrace(Native Method) 

奇怪的是,當我將其中一個測試註釋掉後,第二個測試沒有任何變化就可以正常工作。當一個吐司在另一個上面顯示時,濃咖啡似乎變得愚蠢。任何想法如何解決這個問題?

+0

編輯:我已經設法做到這一點,讓測試活動中的公共敬酒和取消()每個吐司後測試它的外觀,但tbh它似乎不是最好的答案。 – Kamajabu

回答

4

NoMatchingViewException你看到意味着你ToastMatcher確實找到TYPE_TOAST根視圖,但無法找到根裏面的請求的視圖(否則,你會得到一個NoMatchingRootException)。

我猜的原因是,Android是沒有顯示的祝酒詞相互的頂部,但其他後之一。因此,它可能是您在烤麪包根中發現的唯一視圖,就是您的第一面烤麪包(您的第二面烤麪包尚未顯示)。因此,在檢查第二個祝酒之前,您將不得不等到您的第一個祝酒消失。這不是微不足道的,不幸的是(見下文),我相信你無法繞過改變產品代碼。

可能的解決方案在https://stackoverflow.com/a/32023568/1059766中給出。基本思想是在顯示烤麪包的之前,將OnAttachStateChangedListener附加到烤麪包的視圖上,並使用該監聽器來跟蹤視圖何時與視圖層次結構相連接和分離。這可以用來實現可以等待吐司消失的自定義IdlingResource

意式咖啡等待事物的方式是通過IdlingResource s。我目前看不到如何創建自定義閒置資源來等待烤麪包而不更改生產代碼。因此,儘管對生產代碼進行必要的更改並不很吸引人,但我所能想到的與上述答案一致。

這就是說,請注意,您的ToastMatcher解決方案(這通常建議在stackoverflow和博客)也不是一個真正可靠的方法來測試吐司。它適用於大多數情況,但並非總是如此。考慮例如下面的代碼片段:

new AsyncTask<Void, Void, Void>() { 
    public void doInBackground(...) { 
     // start background work for 10s (or just Thread.sleep(10000)) 
    } 
}.execute() 
Toast.make(context, R.string.mytoast, Toast.LENGTH_SHORT).show() 

由於咖啡一直等待,直到UI線程和所有異步任務都處於空閒,就會在上面的例子中等待(約)10秒,直到執行isDisplayed()檢查。但在那時,祝酒會消失,因此檢查失敗。我希望這足以說明這種方法的固有問題。從瓦雷拉扎哈羅夫在https://groups.google.com/d/msg/android-test-kit-discuss/uaHdXuVm-Bw/cuQASd3PdpgJ下面的語句似乎證實有沒有簡單的解決方案與咖啡,以測試祝酒:

Short answer: Unfortunately, there is no thread-safe way of doing this in Android, so we don't provide this capability in Espresso.
Details: The way Toasts are implemented makes it possible to detect a toast has been displayed. However there is no way to see if a Toast has been requested, thru a call to show()) or to block between the period of time between show() and when the toast has become visible. This is opens up unresolvable timing issues (that you can only address thru sleep & hope) [...].

然後扎哈羅夫還提出一些掛鉤添加到生產代碼(據我理解) 。因此,我猜想基於一些生產代碼掛鉤添加一個IdlingResource真的是你可以做的最好的(這也可能使你的吐司測試更穩定一般,因爲你可以按照Zakharov的概述測試你的吐司)。

0

問題是,當您正在測試第二個時,先前顯示的烤麪包片正在顯示。可以通過使一個用於測試的mToast成員變量可見解決這個問題,並用它來取消任何活性吐司在@After,像這樣:

當顯示吐司(被測活動生產代碼):

@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) 
Toast mToast; 

private void showToast(final String text) { 
    mToast = Toast.makeText(this, text, Toast.LENGTH_LONG); 
    mToast.show(); 
} 

測試代碼(在相同的封裝測試的代碼):

@After 
    public void tearDown() { 
     // Remove any toast message that is still shown: 
     Toast toast = mActivityRule.getActivity().mToast; 
     if (toast != null) { 
      toast.cancel(); 
     } 
    } 

要求你改變生產代碼一點點,但使用01如果您在其他地方使用成員變量,則最新版本的Android Studio中的將發生錯誤。