[譯] 使用 Espresso 隔離測試視圖

使用 Espresso 隔離測試視圖

在這篇文章裏,我將會告訴你爲什麼而且如何使用 Espresso 在 Android 設備上測試你的自定義視圖。javascript

你可使用 Espresso 來一次性測試全部界面或流程。這些測試用例會啓動某個頁面,並像用戶通常執行操做,包括等待數據的加載或跳轉到其餘頁面。前端

這樣作是很是有用的,由於你須要端到端的測試用例來驗證常見的用戶使用流程。這些自動化測試應該按期地執行,從而能夠節約手工 QA 的時間來進行探索性測試。java

即使如此,這些不是能夠頻繁運行的測試。運行一整套可能會花費數小時的時間(想象一下驗證媒體內容的脫機同步),因此你能夠選擇在夜間運行它們。react

這很困難,由於這些類型的測試包含了多個潛在的故障點。理想狀況是,當某個測試失敗時,你會但願它是因爲單個邏輯斷言而致使的。android

大多數(或者說不少)能夠引入的迴歸測試點都在 UI 上。這些問題極可能是十分細微的,以致於咱們在添加新特性時並不會注意到,可是敏銳的 QA 團隊卻每每能夠。ios

這樣就浪費太多時間了。git

你能作些什麼?

讓咱們來看下如何使用 Espresso 來測試正確地綁定了數據的視圖。github

在 Novoda 裏,咱們編寫的大多數視圖都是繼承自 Android 已有的 View 和 ViewGroup 類。這些視圖通常只會暴露了一到兩個方法用來綁定回調函數和數據對象/視圖模型,以下所示:後端

public class MovieItemView extends RelativeLayout {  
  private TextView titleTextView;
  private Callback callback;

  public void attach(Callback callback) {
    this.callback = callback;
  }

  public void bind(Movie movie) {
    titleTextView.setText(movie.name());
    setOnClickListener(new OnClickListener() {
      @Override 
      public void onClick(View v) {
        callback.onClick(movie);
      }
    });
  }
}複製代碼

他們將 UI 的邏輯部分組合在一塊兒,而且一般還包含來自業務領域的命名規範。在
Novoda 的頁面佈局中你不多會看到「原始」的 Android 視圖。ide

讓咱們使用 BDD 風格來編寫這些視圖測試,好比「當 MovieItemView 被綁定到 Edward Scissorhands 上,標題就被設置成 Edward Scissorhands」或者「MovieItemView 被綁定到 Edward Scissorhands 上,當點擊視圖時,onClick(Edward Scissorhands) 就會被調用」,等等。(譯者注:BDD(Behaviour Driven Development),傾向於斷言被測對象的行爲特徵而非輸入輸出。一個典型的 BDD 的測試用例包活完整的三段式上下文,測試大多能夠翻譯爲 Given-When-Then 的格式,即某種場景下,發生了事件,致使了什麼結果。)

難道不能使用單元測試來捕獲這些問題嗎?

若是你正在使用像 MVP 或者 MVVM 這樣可被單元測試的表現模式,爲何還須要 Espresso 來運行這些測試呢?

首先,讓咱們來看一下展現信息的流程而且描述一下目前所能作的測試,而後再看看使用 Espresso 測試能多作些什麼。

  • Presenters 訂閱發送事件的數據生成器

  • 事件能夠處於加載中空閒錯誤狀態,而且可能帶有要展現的數據

  • Presenters 將使用 display(List<Movie>)displayCachedDataWhileLoading(List<Movie>)displayEmptyScreen() 等方法將這些事件轉發給「displayers」(MVP 中的「View」)。

  • displayers 的具體實現類將顯示/隱藏 Android 視圖,並執行諸如 moviesView.bind(List<Movie>) 之類的操做

你能夠對 presenters 進行單元測試,驗證是否調用了 displayers 正確的方法而且帶有正確的參數。

你能夠用相同的方式測試 displayers 嗎?是的,你是能夠模擬 Android 視圖,並驗證是否調用了正確的方法。但這樣的粒度並非咱們想要的:

  • displayer 可能確實構建或更新了 RecyclerView 或 ViewPager 適配器,但這並不表明顯示了正確的內容。

  • Android 視圖是經過在代碼中加載 XML(佈局和樣式)設置的;驗證方法的調用不足以斷言顯示的內容是否正確

設置測試用例

就從使用 espresso-support 這個庫開始吧。

在你的 build.gradle(JCenter 可用)裏添加依賴

debugCompile 'com.novoda:espresso-support-extras:0.0.3'  
androidTestCompile 'com.novoda:espresso-support:0.0.3'複製代碼

extras 依賴包中包含了 ViewActivity,在測試時須要將其添加到你的應用中。你能夠在該 Activity 持有想要使用 Espresso 測試的單一視圖。

核心部分(包含自定義測試規則)只須要做爲 androidTest 依賴中的一部分。

ViewTestRule 使用方法與 ActivityTestRule 相似。只不過是將傳遞的參數從想要啓動的 Activity 類替換成了包含你想要測試的視圖的佈局文件:

@RunWith(AndroidJUnit4.class)publicclassMovieItemViewTest{  
  @Rule
  public ViewTestRule<MovieItemView> viewTestRule=newViewTestRule<>(R.layout.test_movie_item_view);
  ...複製代碼

你可使用 ViewTestRule<MovieItemView> 指定根佈局的視圖類型。

ViewTestRule 繼承了 ActivityTestRule<ViewActivity>,因此它總會打開 ViewActivitygetActivityIntent() 被重寫了,因此你能夠將 R.layout.test_movie_item_view 做爲 Intent 的附加數據傳遞給 ViewActivity

你能夠在測試中使用 Mockito 代替回調函數。

@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();

@Mock
MovieItemView.Listener movieItemListener;

@Before
publicvoidsetUp(){  
  MovieItemView view = viewTestRule.getView();
  view.attachListener(movieItemListener);
  ...
 }複製代碼

ViewTestRule 有一個 bindViewUsing(Binder) 方法,該方法會返回視圖的引用,以便你與之進行交互。當你使用 viewTestRule.getView() 直接訪問視圖時,你會但願與視圖的全部交互都是在主線程上執行的,而非測試線程。

@Before
public void setUp() {  
  MovieItemView view = viewTestRule.getView();
  view.attachListener(movieItemListener);
  viewTestRule.bindViewUsing(new ViewTestRule.Binder<MovieItemView>() {
    @Override
    public void bind(MovieItemView view) {
      view.bind(EDWARD_SCISSORHANDS);
    }
  });
}複製代碼

準備測試

從用戶的角度上來看,應用其實只作了兩件事情:

  • 展現信息

  • 響應用戶的操做

要爲這兩種狀況編寫測試,你能夠先從使用標準的 Espresso ViewMatchers 和 ViewAssertions 語句斷言是否顯示正確的信息開始:

@Test
public void titleSetToMovieName() {  
  onView(withId(R.id.movie_item_text_name))
      .check(matches(withText(EDWARD_SCISSORHANDS.name)));
}複製代碼

接着,你應該確保用戶的操做觸發了正確的點擊事件,而且具備正確的參數:

@Test
public void clickMovieItemView() {  
  onView(withClassName(is(MovieItemView.class.getName())))
      .perform(click());

  verify(movieItemListener)
      .onClick(eq(EDWARD_SCISSORHANDS));
}複製代碼

到這裏就完成了,但願這些知識對你有用。

在接下來的文章裏,我會介紹如何使用 Espresso 測試視圖時支持 TalkBack 服務(譯者注:Talkback 是一款由谷歌官方開發的系統工具軟件,它的定位是幫助盲人或者有視力障礙的用戶提供語言輔助)。


掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 AndroidiOSReact前端後端產品設計 等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃

相關文章
相關標籤/搜索