Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Android SDK
Code

Kiếm tra giao diện người dùng Android cùng Espresso

by
Difficulty:IntermediateLength:LongLanguages:

Vietnamese (Tiếng Việt) translation by Andrea Ho (you can also view the original English article)

Trong bài này, bạn sẽ tìm hiểu cách viết các phần test giao diện người dùng với framework Espresso và làm sao tự động hóa quy trình làm việc của bạn, thay cho quy trình thủ công nhàm chán và lỗi thời.

Espresso là một framework test để xây dựng các phần test giao diện người dùng trong Android. Theo tài liệu chính thức, bạn có thể:

Sử dụng Espresso để viết các phần test giao diện người dùng Android ngắn gọn, tốt và đáng tin.

1. Tại sao sử dụng Espresso?

Một trong những vấn đề với việc test thủ công là tốn thời gian và thực hiện rất nhàm chán. Ví dụ: để test màn hình đăng nhập (theo cách thủ công) trong ứng dụng Android, bạn sẽ phải thực hiện các thao tác sau:

  1. Khởi chạy ứng dụng.
  2. Di chuyển đến màn hình đăng nhập.
  3. Xác nhận nếu usernameEditTextpasswordEditText hiển thị.
  4. Nhập tên người dùng và mật khẩu tương ứng.
  5. Xác nhận xem liệu nút đăng nhập có hiển thị và sau đó nhấp vào nút đăng nhập đó.
  6. Kiểm tra xem các view có được hiển thị chính xác không khi đăng nhập thành công hoặc thất bại.

Thay vì mất thời gian để test thủ công ứng dụng của bạn, thì sẽ là tốt hơn khi dành thời gian viết code để giúp cho ứng dụng của chúng ta nổi bật so với các ứng dụng khác! Và, mặc dù việc test thủ công tẻ nhạt và khá chậm, thì vẫn có lỗi và có lẽ bạn sẽ bỏ qua những trường hợp chưa thể phát hiện.

Một số ưu điểm của test tự động gồm có:

  • Các test tự động thực hiện chính xác các trường hợp test giống nhau mỗi khi chúng được thực thi.
  • Nhà phát triển có thể nhanh chóng phát hiện vấn đề nhanh chóng trước khi chúng được gửi đến nhóm QA.
  • Việc này có thể tiết kiệm rất nhiều thời gian, không giống như test thủ công. Với thời gian tiết kiệm, các kỹ sư phần mềm và nhóm QA có thể dành nhiều thời gian hơn cho các nhiệm vụ mang tính thách thức và bổ ích.
  • Đạt được phạm vi test rộng hơn, mang đến một ứng dụng có chất lượng tốt hơn.

Trong hướng dẫn này, chúng ta sẽ tìm hiểu về Espresso qua việc tích hợp nó vào một dự án Android Studio. Chúng ta sẽ viết các test giao diện người dùng cho màn hình login và RecyclerView và chúng ta sẽ học về về các ý định test.

Chất lượng không phải là một hành động, đó là một thói quen. - Pablo Picasso

2. Điều kiện cần có

Để có thể làm theo hướng dẫn này, bạn sẽ cần:

  • hiểu biết cơ bản về core API Android và Kotlin
  • Android Studio phiên bản 3.1.3 trở lên
  • Plugin Kotlin phiên bản 1.2.51 trở lên

Dự án mẫu (trong Kotlin) cho hướng dẫn này trên repo GitHub của chúng tôi, nó giúp bạn có thể dễ dàng theo dõi.

3. Tạo một dự án Android Studio

Khởi động Android Studio 3 và tạo một dự án mới có activity rỗng gọi là MainActivity. Hãy chắc rằng có hỗ trợ Kotlin.

Create Android Project dialog

4. Thiết lập Espresso và AndroidJUnitRunner

Sau khi tạo dự án mới, hãy đảm bảo thêm các dependency (phần phụ thuộc) sau đây từ Android Testing Support Library trong build.gradle của bạn (mặc dù Android Studio đã bổ sung sẵn cho chúng ta). Trong hướng dẫn này, chúng tôi đang sử dụng thư viện Espresso phiên bản 3.0.2 mới nhất (thời điểm viết bài).

Chúng tôi cũng bao gồm instrumentation AndroidJUnitRunner:

Một Instrumentation chạy test JUnit3 và JUnit4 đối với gói Android (ứng dụng).

Lưu ý rằng Instrumentation chỉ đơn giản là một class cơ sở để triển khai instrumentation code của ửng dụng.

Vô hiệu animation

Việc đồng bộ hóa Espresso, không biết làm sao để đợi một animation kết thúc, nó có thể khiến vài test thất bại - nếu bạn cho phép animation diễn ra trên thiết bị test. Để tắt animation trên thiết bị test của bạn, hãy đi tới Settings > Developer Options và tắt tất cả các tùy chọn sau trong mục "Drawing":

  • Quy mô animation của cửa sổ
  • Quy mô animation của chuyển tiếp
  • Quy mô thời lượng animation

5. Viết test đầu tiên của bạn trong Espresso

Đầu tiên, chúng tôi bắt đầu test màn hình Login. Dưới đây là cách quy trình đăng nhập bắt đầu: người dùng khởi chạy ứng dụng và màn hình đầu tiên được hiển thị chứa duy nhất một nút Login. Nhấn nút Login sẽ mở ra màn hình LoginActivity. Màn hình này chỉ chứa hai EditTexts (username và password) và nút Submit.

Dưới đây là bố cục MainActivity của chúng tôi:

MainActivity layout

Dưới đây là bố cục LoginActivity của chúng tôi:

Bây giờ hãy viết một test cho class MainActivity. Đi đến class MainActivity của bạn, di chuyển con trỏ đến tên MainActivity và nhấn Shift-Control-T. Chọn Create New Test ... trong menu popup.

Create new test dialog

Nhấn nút OK và một hộp thoại khác xuất hiện. Chọn thư mục androidTest và nhấp vào nút OK một lần nữa. Lưu ý rằng vì chúng ta đang viết một instrumentation test (test đặc thù của Android SDK), các test case nằm trong thư mục androidTest/java.

Bây giờ, Android Studio đã tạo thành công một class test cho chúng ta. Phía trên tên class, bổ sung chú thích này: @RunWith (AndroidJUnit4::class).

Chú thích này thông báo rằng tất cả các test trong class này là các test dành riêng cho Android.

Test các activity

Bởi vì chúng tôi muốn test một Activity, chúng tôi cần một chút thiết lập. Chúng ta cần phải thông báo cho Espresso biết Activity nào cần mở hoặc khởi động trước khi thực hiện và loại bỏ sau khi thực hiện bất kỳ phương thức test nào.

Lưu ý rằng chú thích @Rule có nghĩa rằng đây là một quy tắc test JUnit4. Các quy tắc test JUnit4 được vận hành trước và sau mỗi phương thức test (được chú thích bằng @Test). Trong kịch bản này, chúng tôi muốn khởi chạy MainActivity trước mỗi phương thức test và sau đó huỷ bó nó.

Chúng tôi cũng bổ sung chú thích @JvmField Kotlin. Đơn giản chỉ hướng dẫn trình biên dịch không tạo ra các getters và setters cho thuộc tính và thay vào đó hiển thị nó thành một field đơn giản.

Dưới đây là ba bước chính trong khi test bằng Espresso:

  • Tìm widget (TextView hoặc Button) mà bạn muốn test.
  • Hãy thực hiện một hoặc nhiều hoạt động trên widget đó.
  • Xác minh hoặc test xem liệu hiện giờ widget đó có ở trạng thái ổn định hay không.

Các kiểu chú thích sau đây có thể được áp dụng cho các phương thức được sử dụng bên trong class test.

  • @BeforeClass: báo hiệu rằng phương thức static có chú thích này phải được thực thi một lần trước tất cả các test trong class. Có thể sử dụng để tạo một kết nối tới một database.
  • @Before: báo hiệu rằng phương thức có chú giải này phải được thực thi trước mỗi phương thức test trong class.
  • @Test: báo hiệu rằng phương thức có chú thích này nên chạy như một trường hợp test.
  • @After: báo hiệu rằng phương thức có chú giải này nên chạy sau mỗi phương thức test.
  • @AfterClass: báo hiệu rằng phương thức có chú thích này nên chạy sau khi tất cả các phương thức test trong class đã được chạy. Ở đây, chúng tôi thường hoàn tất các tài nguyên đã được mở ra trong @BeforeClass.

Tìm một View qua onView()

Trong file bố cục MainActivity của chúng tôi, chúng tôi chỉ có một tiện ích con — nút Login. Hãy test một kịch bản mà người dùng sẽ tìm thấy nút đó và nhấp vào nút đó.

Để tìm các widget trong Espresso, chúng ta sử dụng phương thức tĩnh onView() (thay vì findViewById()). Kiểu tham số mà chúng tôi cung cấp cho onView() là một Matcher. Lưu ý rằng API của Matcher không phải của Android SDK mà từ Hamcrest Project. Thu viện matcher của Hamcrest nằm trong thư viện Espresso mà chúng tôi đã lấy về thông qua Gradle.

OnView(withId(R.id.btn_login)) sẽ trả về ViewInteraction cho View có ID là R.id.btn_login. Trong ví dụ trên, chúng ta đã sử dụng withId() để tìm kiếm một widget có id cho trước. Những matcher khác mà ta có thể sử dụng là:

  • withText(): trả về một matcher phù hợp với TextView dựa trên giá trị thuộc tính text của nó.
  • withHint(): trả về một matcher phù hợp với TextView dựa trên giá trị thuộc tính hint của nó.
  • withTagKey(): trả về một matcher phù hợp với View dựa trên các khóa tag.
  • withTagValue(): trả về một matcher phù hợp với View dựa trên các giá trị thuộc tính tag.

Đầu tiên, hãy test xem nút này có thực sự được hiển thị trên màn hình hay không.

Ở đây, chúng tôi chỉ xác nhận nếu nút có id là (R.id.btn_login) hiển thị đến người dùng, vì vậy chúng tôi sử dụng phương thức check() để xác nhận xem View cơ bản có một trạng thái ổn định hay không - trong trường hợp này, nếu nó có hiển thị.

Phương thức static matcher() trả về một ViewAssertion chung để khẳng định rằng một view tồn tại trong hệ thống phân cấp view và được kết hợp bởi view matcher trước đó. View matcher đó có được isDisplayed() trả về. Như tên gọi của phương thức, isDisplayed() là một matcher phù hợp với các view hiện đang được hiển thị trên màn hình cho người dùng. Ví dụ, nếu chúng ta muốn test xem liệu một nút có được kích hoạt, chúng ta chỉ cần chuyển isEnabled() đến matches().

Các view matcher phổ biến khác mà chúng ta có thể chuyển vào phương thức matches() là:

  • hasFocus(): trả về một matcher phù hợp với các view hiện đang được tập trung.
  • isChecked(): trả về một matcher nhận một view chỉ khi view này là CompoundButton (hoặc subtype của) và ở trạng thái checked. Trái ngược với phương thức này isNotChecked().
  • isSelected(): trả về một matcher phù hợp với các view đã chọn.

Để chạy test, bạn có thể nhấp vào hình tam giác màu xanh lục bên cạnh phương thức hoặc tên class. Nhấp vào tam giác màu xanh lục bên cạnh tên class sẽ chạy tất cả các phương thức test trong class đó, trong khi đó hình tam giác bên cạnh một phương thức chỉ chạy test cho chính phương thức đó.

MainActivityTest class

Hoan hô! Test của chúng tôi đã thành công!

Android Studio test result toolbar

Thực hiện các thao tác trên một View

Trên đối tượng ViewInteraction được trả về bằng cách gọi onView(), chúng ta có thể mô phỏng các hành động mà người dùng có thể thực hiện trên một widget. Ví dụ, chúng ta có thể mô phỏng một hành động click chỉ bằng cách gọi phương thức static click() trong class ViewActions. Hành động này sẽ trả về một đối tượng ViewAction.

Tài liệu cho biết rằng ViewAction:

Chịu trách nhiệm thực hiện tương tác với thành phần View đã cho.

Chúng tôi triển khai sự kiện Click bằng cách gọi perform() trước tiên. Phương thức này thực hiện hành động đã cho trên View được chọn bởi view matcher hiện tại. Lưu ý rằng chúng ta có thể truyền cho một hành động duy nhất hoặc một danh sách các hành động (xử lý theo thứ tự). Ở đây, chúng tôi đã gán cho nó phương thức click(). Các hành động khác có thể là:

  • typeText() để giả lập việc nhập văn bản vào EditText.
  • clearText() để mô phỏng hành động xóa văn bản trong EditText.
  • doubleClick() để mô phỏng hành động nhấp đúp vào View.
  • longClick() để giả lập hành động nhấp chuột vào View.
  • scrollTo() để mô phỏng hành động cuộn một ScrollView đến một View cụ thể có thể nhìn thấy.
  • swipeLeft() để mô phỏng hành động vuốt sang phải - sang trái ngang qua trung tâm của một View.

Nhiều mô phỏng khác có thể được tìm thấy bên trong class ViewActions.

Xác thực với View Assertion

Hãy hoàn thành phần test của chúng ta, để xác nhận rằng màn hình LoginActivity được hiển thị bất cứ khi nào nút Login được nhấp vào. Mặc dù chúng ta đã thấy cách sử dụng check() trên ViewInteraction, nhưng hãy sử dụng nó một lần nữa, truyền cho nó một ViewAssertion khác.

Bên trong file bố cục LoginActivity, ngoài EditTexts và một Button, chúng ta cũng có một TextView với ID là R.id.tv_login. Vì vậy, chúng tôi chỉ cần thực hiện test để xác nhận rằng TextView hiển thị với người dùng.

Bây giờ bạn có thể chạy lại test!

Android Studio test result toolbar

Các test của bạn sẽ thành công nếu bạn làm theo tất cả các bước thật chính xác.

Đây là những điều đã xảy ra trong quá trình thực hiện các test của chúng tôi:

  1. Khởi chạy MainActivity bằng field activityRule.
  2. Đã xác minh khi nút Login (R.id.btn_login) hiển thị ( isDisplayed() ) với người dùng.
  3. Mô phỏng hành động nhấp chuột (click()) trên nút đó.
  4. Được xác minh khi LoginActivity hiển thị cho người dùng bằng cách test xem một TextView có id R.id.tv_login trong LoginActivity có hiển thị hay không.

Bạn luôn có thể tham khảo cheat sheet của Espresso để xem các view matcher khác nhau, xem actions và view assertion.

6. Kiểm tra màn hình LoginActivity

Đây là LoginActivity.kt của chúng tôi:

Trong đoạn code bên trên, nếu username là "chike" và pasword là "password", thì đăng nhập thành công. Đối với bất kỳ dữ liệu nhập nào khác, thì không hoạt độngi. Giờ chúng ta hãy viết một test Espresso cho trường hợp này!

Đến LoginActivity.kt, di chuyển con trỏ đến tên LoginActivity và nhấn Shift-Control-T. Chọn Create New Test ... trong menu popup. Thực hiện theo quy trình tương tự như chúng ta đã thực hiện đối với MainActivity.kt và nhấp vào nút OK.

Class test này rất giống với class đầu tiên của chúng ta. Nếu chúng ta chạy test, màn hình LoginActivity được mở ra. Username và password được nhập vào các field R.id.et_usernameR.id.et_password tương ứng. Tiếp theo, Espresso sẽ nhấp vào nút Submit (R.id.btn_submit). Nó sẽ đợi cho đến khi tìm thấy một View với id R.id.tv_login với nội dung đọc được là Success.

7. Test một RecyclerView

RecyclerViewActions là class hiển thị một tập hợp các API để vận hành trên RecyclerView. RecyclerViewActions là một phần của kết quả riêng biệt bên trong expresso-contrib, nó cũng nên được bổ sung vào build.gradle:

Lưu ý rằng kết quả này cũng chứa API cho UI để test các điều hướng kiểu drawer thông qua DrawerActionsDrawerMatchers.

Để click vào một mục ở bất kỳ vị trí nào trong RecyclerView, chúng ta gọi actionOnItemAtPosition(). Chúng ta phải cho nó một kiểu item. Trong trường hợp này, mục này là class ViewHolder nằm trong RandomAdapter. Phương thức này cũng có hai tham số; đầu tiên là position và thứ hai là action (ViewActions.click()).

RecyclerViewActions khác có thể được thực hiện là:

  • actionOnHolderItem(): thực hiện một ViewAction trên một view phù hợp bởi viewHolderMatcher. Việc này giúp chúng ta kết hợp nó với nội dung bên trong ViewHolder thay cho position (vị trí).
  • scrollToPosition(): trả về một ViewAction để cuộn RecyclerView đến một position.

Tiếp theo (khi màn hình "add note screen" mở ra), chúng ta sẽ nhập văn bản ghi chú và lưu lại. Ta không cần đợi màn hình mới mở ra — Espresso sẽ tự động thực hiện việc này. Nó đợi cho đến khi tìm thấy một View với id là R.id.add_note_title.

8. Test intents

Espresso sử dụng một sản phẩm khác có tên là espresso-intents để test các intent. Sản phẩm này chỉ là một phần mở rộng cho Espresso, tập trung vào việc xác thực Intent. Hãy xem ví dụ.

Đầu tiên, chúng tôi phải pull thư viện intpresso-intents cho dự án của chúng tôi.

IntentsTestRule mở rộng ActivityTestRule, vì vậy cả hai đều có các behaviour tương tự. Tài liệu mô tả như sau:

Class này là phần mở rộng của ActivityTestRule, nó khởi tạo Espresso-Intents trước mỗi test được chú thích bằng Test và phát hành Espresso-Intents sau mỗi lần chạy test. Activity sẽ kết thúc sau mỗi lần test và quy tắc này có thể được sử dụng theo cách tương tự như ActivityTestRule.

Tính năng nổi bất để phân biệt chính là nó có các chức năng bổ sung cho việc test startActivity()startActivityForResult() với các mocks và stubs.

Bây giờ chúng ta sẽ test một kịch bản mà người dùng sẽ nhấp vào một nút (R.id.btn_select_contact) trên màn hình để chọn một liên lạc từ danh sách liên lạc của điện thoại.

Ở đây, chúng tôi đang sử dụng intending() từ thư viện espresso-intents để thiết lập một stub một phản hồi giả cho yêu cầu ACTION_PICK. Dưới đây là những gì xảy ra trong PickContactActivity.kt khi người dùng nhấp vào nút có id là R.id.btn_select_contact để chọn một liên hệ.

intending() nhận được trong một Matcher khớp với intent cho phản hồi cần được cung cấp. Nói cách khác, Matcher xác định yêu cầu mà bạn quan tâm. Trong trường hợp này, chúng ta sử dụng hasAction() (một phương thức trợ giúp trong IntentMatchers) để tìm yêu cầu ACTION_PICK của chúng ta. Sau đó chúng ta gọi respondWith(), phương thức này thiết lập kết quả cho onActivityResult(). Trong trường hợp này, kết quả là Activity.RESULT_OK, bằng việc mô phỏng người dùng chọn một liên hệ từ danh sách.

Sau đó chúng ta mô phỏng hành động nhấn vào nút select button (chọn liên hệ), gọi là startActivityForResult(). Lưu ý rằng stub của chúng tôi đã gửi câu trả lời giả lập tới onActivityResult().

Sau cùng, chúng ta sử dụng phương thức trợ giúp intended() chỉ để xác nhận rằng các lệnh gọi startActivity()startActivityForResult() được thực hiện với thông tin đúng.

Tổng kết

Trong hướng dẫn này, bạn đã học cách dễ dàng sử dụng framework Espresso để test dùng trong dự án Android Studio nhằm tự động hóa quy trình test của bạn.

Tôi đề nghị bạn nên xem tài liệu chính thức để hiểu thêm về cách viết test cho giao diện người dùng bằng Espresso.

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.