+2

Unit Test cho người mới bắt đầu - Dễ dàng hơn khi dùng Mockito

Trong quá trình xây dựng hay phát triển dự án có những lúc chúng ta cần phải viết Unit Test, có công cụ nào làm cho việc xây dựng Unit Test của bạn trở lên thuận tiện và nhanh chóng hơn không ? Bài viết này mình đề cập tới việc sử dụng một framework phổ biến hiện nay : Mockito Mình trích một câu nói về framework này như sau :

Mockito can be used as a mocking framework in Android. It allows us to fake external interactions

1. Chức năng của Mockito

Qua câu trích dẫn trên ít nhiều chúng ta cũng đã hình dung được chức năng của Mokito rồi, đó là " Giả lập những tương tác bên trong". Vậy tương tác bên trong là những điều gì ? Khi viết unit test cho các method và class trong một chức năng nhỏ nào đó, trong nhiều trường hợp method được kiểm thử gọi đến những phương thức của interface, mà interface này chưa được cài đặt (implementing) bởi bất cứ lớp đối tượng nào. Khi đó, trong mã lệnh unit test cần giả lập (mocking) các hàm của interface được sử dụng. Để giả lập chức năng của các hàm trong interface, tạo giả một đối tượng cài đặt interface, giả lập lời gọi hàm đến đối tượng giả này và chỉ định kết quả trả lại cho lời gọi hàm giả lập đó

2. Một số lưu ý khi dùng Mockito

  • Không test Activity hay Fragment. Chúng được test với một framework test UI tự động khác.
  • Không test Getter and Setter, chỉ test khi trong đó có chứa thêm 1 method khác hay logic . Dĩ nhiên điều này ít khi sảy ra rồi 😄
  • Không kiểm tra các method liên quan đến JVM hoặc Android OS.

Tiếp theo chúng ta hãy sử dụng Mockito một chút xem sao nhé.

3. Setup and Build sample test

  • Setup Mockito vào project của bạn, thêm đoạn code dưới đây vào trong build.gradle :
dependencies {

  androidTestCompile 'org.mockito:mockito-core:2.+'
  androidTestCompile 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'
  androidTestCompile ('junit:junit:4.12') {
    exclude module: 'hamcrest-core'
  }
  
}

Khi dùng androidTestCompile thì thư viện này sẽ không được thêm vào main project mà chỉ có chức năng khi run test. Mình chỉ import mockito-core mà không phải toàn bộ lib mockito bởi vì nó không tương thích tất cả với JUnit 4, và chúng ta loại bỏ hamcrest-core để tránh 1 vài issues khác.

  • Sample Test :

MainActivity.java Chúng ta sẽ làm một ví dụ nhỏ : dùng Mockito để giả lập xem chức năng của method createNewIntent(Context context, String username) chạy có chính xác hay không. Nội dung code trong MainActivity như dưới đây.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public static Intent createNewIntent(Context context, String username) {
        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra("USER", username);
        return intent;
    }
}

Tiếp theo bạn hãy vào package (Android Test) của project và thêm 1 class MainTest.java

MainTest.java

public class MainTest {

    @Mock
    Context mContext;

    @Test
    public void testIntentCreated() {
        Intent intent = MainActivity.createNewIntent(mContext, "viet");
        Assert.assertNotNull(intent);
    }
}

Annotation @Mock khai báo mock cho đối tượng và ở đây chỉ có Context thôi. Khi bạn right-click >> Run testIntentCreated Sẽ nhận được kết quả :

Chúng ta đã thiếu sót ở đâu đó, dẫn đến việc khởi tạo Context bị null. À bởi vì mình còn thiếu việc định danh cho class MainTest.java sẽ khởi chạy như MockitoJUnitRunner . Để làm điều này mình cần thêm annotation sau @RunWith(MockitoJUnitRunner.class) trên cùng của class. Bây giờ khi Run Test lại chúng ta được kết quả :

Việc dựng unit test cho method createNewIntent(Context context, String username) chưa được hoàn thiện vì thiếu việc kiểm tra xem giá trị truyền vào intent và đến lúc get ra có được trùng khớp hay không ? Chúng ta cần thêm vào class MainTest.java như sau:

@RunWith(MockitoJUnitRunner.class)
public class MainTest {

    @Mock
    Context mContext;

    @Test
    public void testIntentCreated() {
        Intent intent = MainActivity.createNewIntent(mContext, "viet");
        Assert.assertNotNull(intent);

        Bundle bundle = getBundle(intent);
        Assert.assertEquals("viet", bundle.getString("USER"));
    }
    private Bundle getBundle(Intent intent) {
        return intent.getExtras();
    }
}

Assert.assertEquals("viet", bundle.getString("USER")); Lệnh này dùng để check xem 2 giá trị String str1 và str2 có trùng khớp nhau hay không. Hay đúng hơn Str1 chính là giá trị mà chúng ta giả lập (điều mà chúng ta dự đoán trước) để so sánh với Str2 được lấy ra từ Bundle. Lúc này bạn có thể tự Run Test lại để kiểm chứng.

4. Method hay được dùng

  • Mockito.when(T methodCall) : dùng để giả lập một lời gọi hàm nào đó được sử dụng bên trong method đang được kiểm thử
  • Mockito.anyString(), Mockito.anyInt() : thường được dùng khi mock các method có tham số, mà bạn không xác định được giá trị của các tham số đó.

Ví dụ chi tiết :

//Khi method_A được gọi thì trả về kết quả là “demoValue”: 
Mockito.when(method_A()).thenReturn("demoValue");
 
//Khi method_B được gọi thì sẽ ném ra một Exception với message “demoError”: 
Mockito.when(method_B()).thenThrow(new Exception("demoError"));
 
//Khi method_C được gọi thì sẽ thực hiện xử lý các lệnh định nghĩa 
// bên trong hàm answer() (hàm này giả lập xử lý của method_C) và 
// trả về kết quả:
Mockito.when(method_C()).thenAnswer(new Answer<String>(){
    public String answer(InvocationOnMock invocation){
        String str = “demoNewAnswer”;
            return str;
        }
    });

Giới Hạn Bất kể framework nào dùng thuận tiện đến đâu cũng có 1 số giới hạn nhất định và Mockito cũng vậy , bạn không test được với những đối tượng sau :

  • Inner classes.
  • Final classes.
  • Anonymous classes.

Bên cạnh đó khi chúng ta ứng dụng hết những chức năng được cung cấp trong Mockito thì chắc chắn rằng Unit Test lúc đó được viết ra rất trơn tru và cover được nhiều case khó, đồng thời chất lượng code cũng tăng lên Level mới đó. ^_^

5. Tổng kết

Bài viết đã giới thiệu qua về Mockito , cách implement, ví dụ và những điểm cần lưu ý khi chúng ta viết một Unit Test. Với những thông tin trên đây, mong rằng bài viết của mình mang lại sự hữu ích cho các bạn mới đang tìm hiểu về việc viết Unit Test. Chúc các bạn ngày càng nâng cao chất lượng code của mình!


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.