Cách test những Fragment một cách đơn lẻ

Tất cả chúng ta đều biết rằng Unit Test là rất quan trọng trong quá trình phát triển dự án. Ngay từ khi bắt đầu chúng ta thấy rằng chúng ta chưa cần phải viết test tích hợp. Việc đầu tiên là phải viết test cho UI. Vậy chúng ta cần gì để sẵn sàng cho việc viết UI Unit tests. Trong Android, UI thường được nằm trong Acitivty. Espressp ActivityTestRule cho phép chúng ta test Activity một cách dễ dàng. Vì vậy chúng ta sử dụng 2 công cụ để test UI trong hầu hết các trường hợp nhưng đôi khi chúng ta cần test những UI nó mang tính chất đơn nhất. Chúng ta không muốn test những toàn thể Activity, chúng ta chỉ muốn test một Fragment hay đơn giản là một View Không phải hầu hết ai cũng ưu chuộng sử dụng Framgment, nhưng trong một số tính huống chúng thực sự hữu dụng mà. Trong một vài ngữ cảnh như chúng ta muốn hiển thị da dạng trên một ứng dụng có newfeed, những ứng dụng có thiết kế UI phức tạp, ví dụ màn hình có phân trang chiều ngang rồi chiều dọc, like, share và điều hướng đến các màn hình khác v..v. Vấn để ở đây là nếu chúng ta muốn test Fragment thì chúng ta cần phải có 1 Activity chứa chúng. Vì vậy để test đc Fragment thì chúng ta phải chạy Activity và quản lí chúng. Cụ thể hơn chúng ta phải : Chạy Activity, chờ cho nó đc tạo ra bởi Android, sau đó thêm Fragment chúng ta muốn test tiếp theo là chờ cho Fragment load. Điều đó là cần thiết để chắc chắn rằng Fragment đã sẵn sàng nhận event khi chúng ta chạy test. Để dễ dàng hơn trong quản lí chúng ta sẽ tạo ra FragmentTestRule, được lấy cảm hứng từ câu trả lời của SO trên Stackoverflow

Cài đặt FragmentTestRule

Thêm vào dependency trong build.gradle file:

androidTestCompile 'com.21buttons:fragment-test-rule:1.0.0'

Vâng chính là nó đó. Bây giờ thì chúng ta có thể viết test cho Fragment rồi. Hãy cùng xem ví dụ về 1 fragment sau.

public class FragmentA extends Fragment {

  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment_a, container, false);
    final TextView textView = view.findViewById(R.id.textView);

    view.findViewById(R.id.button)
        .setOnClickListener((ignore) -> textView.setText(R.string.button_clicked));

    return view;
  }
}

Đây là FragmentA . Fragment này có 1 button và khi một ai đó click thì text sẽ hiển thị lên màn hình. Điều đầu tiên chúng ta cần là add nó là JUnit rule :


@Rule
public FragmentTestRule<?, FragmentA> fragmentTestRule = 
    FragmentTestRule.create(FragmentA.class);

Rule này sẽ đảm bảo với chúng ta rằng FragmenA có một thể hiện và sẵn sàng cho các test case của chúng ta. Giờ thì ta sẽ viết test cho FragmentA như sau:

@Test
public void clickButton() throws Exception {
  onView(withText(R.string.button)).perform(click());

  onView(withText(R.string.button_clicked)).check(matches(isDisplayed()));
}

FragmentTestRule sẽ ẩn những thứ cần thiết để khởi chạy Activity để tập chung test cho Fragment . Nó khởi chạy container của Activity và add Fragment mong muốn vào. Hãy cùng xem một ví dụ khác:

public class FragmentB extends Fragment {

  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment_a, container, false);
    final TextView textView = view.findViewById(R.id.textView);

    view.findViewById(R.id.button)
        .setOnClickListener((ignore) -> textView.setText(((TextInterface) getActivity()).getText()));

    return view;
  }

  public interface TextInterface {
    String getText();
  }
}

Trường hợp này cũng khá giống với ví dụ trên nhưng có một sự khác biệt là nó bị phụ thuộc vào thằng Activity đoạn getText() . Activity phải implement TextInterface nếu không test sẽ crash với ClassCastException . Bạn có thể quản lí nó ở trong FragmentTestRule nhưng sẽ mất công hơn chút. Đầu tiên bạn phải tạo một *Activity * vẻ implement TextInterface

public class DebugActivity extends FragmentActivity implements FragmentB.TextInterface {

  public String getText() {
    return "Hello world!";
  }
}

Nếu bạn không muốn Activity này trong bản release, thì nên add nó vào bản build debug . Giống như Activity, cần phải tạo thêm 1 file AndroidManifest riêng cho bản build debug.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.a21buttons.fragmenttestrule.sample">

  <application>
    <activity android:name=".DebugActivity" />
  </application>
</manifest>

Như vậy chúng ta đã sẵn sàng cho việc viết test rồi. Chúng ta phải nói cho FragmentTestRule biết là nó phải chạy Activity nào.

@Rule
public FragmentTestRule<DebugActivity, FragmentB> fragmentTestRule =
    new FragmentTestRule<>(DebugActivity.class, FragmentB.class);

Sau đó ta viết test như bình thường

@Test
public void clickButton() throws Exception {
  onView(withText(R.string.button)).perform(click());

  onView(withText("Hello world!")).check(matches(isDisplayed()));
}

Bằng cách như vậy ta có thể test đc Fragment một cách độc lập rồi. FragmentTestRule library là thư viện mã nguồn mở vì vậy nếu bạn có khả năng hãy cùng tham gia và đóng góp cho nó ngày càng hoàn thiện hơn nhé. Bạn có thể checkout code của nó ở đây Thông qua bài viết bày tôi mong mang lại cho các bạn một chút kiến thức trong việc viết UnitTest trong Android. Hẹn gặp lại các bạn trong các bài viết sau. Nguồn 21Buttons