Linux中國

編寫 android 測試單元該做的和不該做的事

在本文中, 我將根據我的實際經驗,為大家闡述一個編寫測試用例的最佳實踐。在本文中我將使用 Espresso 編碼, 但是它們可以用到單元測試和 儀器測試 instrumentation test 當中。基於以上目的,我們來研究一個新聞程序。

以下內容純屬虛構,如有雷同純屬巧合 😛

一個新聞 APP 應該會有以下這些 activity。

  • 語言選擇 - 當用戶第一次打開軟體, 他必須至少選擇一種語言。選擇後,選項保存在共享偏好中,用戶跳轉到新聞列表 activity。
  • 新聞列表 - 當用戶來到新聞列表 activity,將發送一個包含語言參數的請求到伺服器,並將伺服器返回的內容顯示在 recycler view 上(包含有新聞列表的 id, news_list)。 如果共享偏好中未存語言參數,或者伺服器沒有返回一個成功消息, 就會彈出一個錯誤對話框並且 recycler view 將不可見。如果用戶只選擇了一種語言,新聞列表 activity 有個 「Change your Language」 的按鈕,或者如果用戶選擇多種語言,則按鈕為 「Change your Languages」 。 (我對天發誓這是一個虛構的 APP 軟體)
  • 新聞細節 - 如同名字所述, 當用戶點選新聞列表項時將啟動這個 activity。

這個 APP 功能已經足夠,,讓我們深入研究下為新聞列表 activity 編寫的測試用例。 這是我第一次寫的代碼。

/*
    Click on the first news item.
    It should open NewsDetailActivity
     */
    @Test
    public void testClickOnAnyNewsItem() {
        onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
                .actionOnItemAtPosition(1, click()));
        intended(hasComponent(NewsDetailsActivity.class.getName()));
    }

  /**
   * To test the correct text on the button
   */
  @Test
  public void testChangeLanguageFeature() {
    int count = UserPreferenceUtil.getSelectedLanguagesCount();
    if (count == 1) {
      onView(withText("Choose your Language")).check(matches(isDisplayed()));
    } else if (count > 1) {
      onView(withText("Choose your Languages")).check(matches(isDisplayed()));
    }
 ?}

仔細想想測試什麼

在第一個測試用例 testClickOnAnyNewsItem(), 如果伺服器沒有返回成功信息,測試用例將會返回失敗,因為 recycler view 是不可見的。但是這個測試用例的目的並非如此。 不管該用例為 PASS 還是 FAIL,它的最低要求是 recycler view 總是可見的, 如果因某種原因,recycler view 不可見,那麼測試用例不應視為 FAILED。正確的測試代碼應該像下面這個樣子。

 /*
  Click on any news item.
  It should open NewsDetailActivity
   */
  @Test
  public void testClickOnAnyNewsItem() {
    try {
            /*To test this case, we need to have recyclerView present. If we don't have the
            recyclerview present either due to the presence of error_screen, then we should consider
            this test case successful. The test case should be unsuccesful only when we click on a
            news item and it doesn't open NewsDetail activity
            */
      ViewInteraction viewInteraction = onView(withId(R.id.news_list));
      viewInteraction.check(matches(isDisplayed()));
    } catch (NoMatchingViewException e) {
      return;
    } catch (AssertionFailedError e) {
      return;
    }
    //在這裡我們確信,news_list的 recyclerview 對用戶是可見的。
    onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
        .actionOnItemAtPosition(1, click()));
    intended(hasComponent(NewsDetailsActivity.class.getName()));
  }
}

一個測試用例本身應該是完整的

當我開始測試, 我通常按如下順序測試 activity:

  • 語言選擇
  • 新聞列表
  • 新聞細節

因為我首先測試語言選擇 activity,在測試 NewsList activity 之前,總有一種語言已經是選擇好了的。但是當我先測試新聞列表 activity 時,測試用例開始返回錯誤信息。原因很簡單 - 沒有選擇語言,recycler view 不會顯示。注意, 測試用例的執行順序不能影響測試結果。 因此在運行測試用例之前, 語言選項必須是保存在共享偏好中的。在本例中,測試用例獨立於語言選擇 activity 的測試。

@Rule
  public ActivityTestRule activityTestRule =
      new ActivityTestRule(TopicsActivity.class, false, false);

  /*
  Click on any news item.
  It should open NewsDetailActivity
   */
  @Test
  public void testClickOnAnyNewsItem() {
    UserPreferenceUtil.saveUserPrimaryLanguage("english");
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
    try {
      ViewInteraction viewInteraction = onView(withId(R.id.news_list));
      viewInteraction.check(matches(isDisplayed()));
    } catch (NoMatchingViewException e) {
      return;
    } catch (AssertionFailedError e) {
      return;
    }
    onView(allOf(withId(R.id.news_list), isDisplayed())).perform(RecyclerViewActions
        .actionOnItemAtPosition(1, click()));
    intended(hasComponent(NewsDetailsActivity.class.getName()));
 ?}

在測試用例中避免使用條件代碼

現在在第二個測試用例 testChangeLanguageFeature() 中,我們獲取到用戶選擇語言的個數,基於這個數目,我們寫了 if-else 條件來進行測試。 但是 if-else 條件應該寫在你的代碼當中,而不是測試代碼里。每一個條件應該單獨測試。 因此,在本例中,不是只寫一條測試用例,而是要寫如下兩個測試用例。

/**
   * To test the correct text on the button when only one language is selected.
   */
  @Test
  public void testChangeLanguageFeatureForSingeLanguage() {
    //Other initializations
    UserPreferenceUtil.saveSelectedLanguagesCount(1);
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
      onView(withText("Choose your Language")).check(matches(isDisplayed()));
  }

  /**
   * To test the correct text on the button when more than one language is selected.
   */
  @Test
  public void testChangeLanguageFeatureForMultipleLanguages() {
    //Other initializations
    UserPreferenceUtil.saveSelectedLanguagesCount(5); //Write anything greater than 1.
    Intent intent = new Intent();
    activityTestRule.launchActivity(intent);
    onView(withText("Choose your Languages")).check(matches(isDisplayed()));
  }

測試用例應該獨立於外部因素

在大多數應用中,我們與外部網路或者資料庫進行交互。一個測試用例運行時可以向伺服器發送一個請求,並獲取成功或失敗的返回信息。但是不能因從伺服器獲取到失敗信息,就認為測試用例沒有通過。這樣想這個問題 - 如果測試用例失敗,然後我們修改客戶端代碼,以便測試用例通過。 但是在本例中, 我們要在客戶端進行任何更改嗎?- NO

但是你應該也無法完全避免要測試網路請求和響應。由於伺服器是一個外部代理,我們可以設想一個場景,發送一些可能導致程序崩潰的錯誤響應。因此,你寫的測試用例應該覆蓋所有可能來自伺服器的響應,甚至包括伺服器決不會發出的響應。這樣可以覆蓋所有代碼,並能保證應用可以處理所有響應,而不會崩潰。

正確的編寫測試用例與編寫這些測試代碼同等重要。

感謝你閱讀此文章。希望對測試用例寫的更好有所幫助。你可以在 LinkedIn 上聯繫我。還可以在這裡閱讀我的其他文章。

獲取更多資訊請關注我們, 我們發新文章時您將獲得通知。

via: https://blog.mindorks.com/the-dos-and-don-ts-of-writing-test-cases-in-android-70f1b5dab3e1#.lfilq9k5e

作者:Anshul Jain 譯者:kokialoves 校對:jasminepeng

本文由 LCTT 原創編譯,Linux中國 榮譽推出


本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive

對這篇文章感覺如何?

太棒了
0
不錯
0
愛死了
0
不太好
0
感覺很糟
0
雨落清風。心向陽

    You may also like

    Leave a reply

    您的電子郵箱地址不會被公開。 必填項已用 * 標註

    此站點使用Akismet來減少垃圾評論。了解我們如何處理您的評論數據

    More in:Linux中國