[Android] Unit Testing (Robolectric)
Bài đăng này đã không được cập nhật trong 3 năm
Android Unit Testing
I. Giới thiệu.
- Một Unit là một thành phần PM nhỏ nhất mà ta có thể kiểm tra được. Theo định nghĩa này, các hàm (Function), thủ tục (Procedure), lớp (Class), hoặc các phương thức (Method) đều có thể được xem là Unit. Unit test là việc thực hiện test các thành phần nhỏ này.
- Unit Test đòi hỏi kiểm tra viên có kiến thức về thiết kế và code của chương trình nên Unit Test thường do lập trình viên thực hiện. Công đoạn này cần được thực hiện càng sớm càng tốt trong giai đoạn viết code và xuyên suốt chu kỳ PTPM.
- Unit Test cũng đòi hỏi phải chuẩn bị trước các tình huống (test case) hoặc kịch bản (script), trong đó chỉ định rõ dữ liệu vào, các bước thực hiện và dữ liệu mong chờ sẽ xuất ra. Các test case và script này nên được giữ lại để tái sử dụng.
II. Unit test trong Android.
Để có thể thực hiện unit test trên android mình sẽ hướng dẫn các bạn sử dụng Robolectric.
1. Giới thiệu Robolectric:
- là một framework cho phép bạn viết unit test và chạy chúng trên môi trường JVM mà không cần chạy ứng dụng Android một cách trực tiếp trên thiết bị.
2. Sử dụng Robolectric trong Android Studio
-
Mình sẽ hướng dẫn các bạn sử dụng Robolectric viết test case cho 1 project đơn giản.
- Mô tả project: nhập 2 số X và Y nhấn Ok để thực hiện phép tính cộng rồi hiên thị kết quả.
- Thành phần giao diện gồm: 1 activity, 2 edit text (X,Y), 1 textview hiển thị kểt quả, 1 buttom tính kết quả.
-
Thêm thư viện Robolectric vào project bằng cách thêm đoạn mã sau vào file build.gradle.
testCompile 'org.easytesting:fest:1.0.16'
testCompile 'com.squareup:fest-android:1.0.8'
testCompile('org.robolectric:robolectric:3.1.1') {
exclude group: 'commons-logging', module: 'commons-logging'
exclude group: 'org.apache.httpcomponents', module: 'httpclient'
}
- Các file java dùng viết test case trong thư mục sau
- Viết Test Case:
-
Từ bài toán trên mình nghĩ ra 3 test case sau
- Các thành phần giao diện không null.
- Các giá trị nhập vào X và Y phải là 1 số nguyên.
- kết quả trả về phải chính xác (X + Y).
-
Config file test
- Thêm 2 câu lệnh sau vào trước mỗi file test case
-
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
- Bắt đầu 1 test case sử dụng hàm setUp (@Before), ở đây mình đang thực hiện viết test case cho MainActivity.
@Before public void setUp() throws Exception {
// khơi tạo activity
mActivity = Robolectric.buildActivity(MainActivity.class).create().get();
// khởi tạo các thành phần sử dụng trong activity
valueX = (EditText) mActivity.findViewById(R.id.edt_x);
valueY = (EditText) mActivity.findViewById(R.id.edt_y);
result = (TextView) mActivity.findViewById(R.id.tv_sum);
addButton = (Button) mActivity.findViewById(R.id.btn_ok);
}
- Bắt đầu mỗi hàm test thêm ký tự (@Test)
// test case kiểm tra các thành phần giao diện không null
@Test public void testNotNull() throws Exception {
assertNotNull(valueX);
assertNotNull(valueY);
assertNotNull(result);
assertNotNull(addButton);
assertNotNull(mActivity);
}
// test case kiểm tra giá trị nhập vào là 1 số nguyên
@Test public void testInputValue() throws Exception {
assertThat(TextUtils.isDigitsOnly(valueX.getText())).isEqualTo(true);
assertThat(TextUtils.isDigitsOnly(valueY.getText())).isEqualTo(true);
}
// test case kiểm tra kết quả là chính xác khi nhập (8 + -1 = 7)
@Test public void testResult() throws Exception {
valueX.setText("8");
valueY.setText("-1");
addButton.performClick();
assertThat(result.getText().toString().trim()).isEqualTo("7");
}
- Viết code và chạy thử test case.
- code:
public class MainActivity extends Activity {
private EditText valueX, valueY;
private TextView result;
private Button btnSum;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
btnSum.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View view) {
if (TextUtils.isDigitsOnly(valueX.getText()) && TextUtils.isDigitsOnly(valueY.getText()))
{
sum();
}
}
});
}
private void initView() {
valueX = (EditText) findViewById(R.id.edt_x);
valueY = (EditText) findViewById(R.id.edt_y);
result = (TextView) findViewById(R.id.tv_sum);
btnSum = (Button) findViewById(R.id.btn_ok);
}
private void sum() {
int val1 = Integer.parseInt(valueX.getText().toString());
int val2 = Integer.parseInt(valueY.getText().toString());
int answer = val1 + val2;
result.setText(answer + "");
}
}
- Chạy test case: click chuột phải vào file test case chọn Run rồi đợi quá trình test. Lỗi sẽ được hiển thị màu đỏ
III. Đặc điểm
-
Ưu điểm:
- Giúp dev lắm rõ hơn về spec thông quá trình viết test case.
- Kiểm thử được các đoạn code xử lý phức tạp có tính logic cao.
- Giúp hạn chế bug phát sinh trong quá trình chỉnh sửa thay đổi code.
- Cải thiện khả năng maintain khi bàn giao code cho người khác, thể hiện bằng việc người khác chỉ cần đọc testcase là hiểu chức năng của đoạn code.
- Giúp tester cũng có thể tiếp cận bằng cách ngồi nghĩ test case cho dev.
-
Nhược điểm:
- Công đoạn thực hiện mất nhiều thời gian do phải viết testcase.
- Không thích hợp với các đoạn code đơn giản không cần thiết phải kiểm thử.
- Khó khăn cho dev mới, vì phải nghĩ kịch bản trước khi code.
-
Tổng kết:
- Từ các ưu nhược điểm trên chúng ta có thể thấy việc sử dụng Unit test là rất tiện lợi và cần thiết để hạn chế được bug trong quá trình phát triển phần mềm. Chúng ta chỉ sử dụng Unit test trong các xử lý phức tạp dễ phát sinh bug để đảm bảo chất lượng code của mình.
IV.Tài liệu
- Unit test còn có thể test các vấn đề phức tạp như Multiple times như tốc độ truy xuất SQLite, tốc độ request server... Về các vấn đề nâng cao này bạn nào quan tâm thì hãy tìm hiểu thêm nhé.
- Source: http://robolectric.org/
All rights reserved