HomeOur Team
Solutions
# Java Unit Testing - Phần 2 - Giới thiệu JUnit
hai.nguyen
hai.nguyen
March 24, 2021
4 min

1. JUnit là gì?

JUnit là một framework được sử dụng cho mục đích kiểm thử đơn vị (unit testing) cho ngôn ngữ lập trình Java. Nó đóng một vai trò quan trọng trong phát triển phần mềm dựa trên kiểm thử, và là một trong những framework kiểm thử đơn vị được gọi chung là xUnit (Một unit ở đây có thể là một hàm, phép tính, một module, một class – thường thì người ta sẽ sử dụng method để làm unit test).

2. Các tính năng của JUnit

  • JUnit là một framework mã nguồn mở, được sử dụng để viết và chạy kiểm thử.
  • Cung cấp các annotation để định nghĩa các phương thức kiểm thử.
  • Cung cấp các Assertion để kiểm tra kết quả mong đợi.
  • Cung cấp các test runner để thực thi các test script.
  • Test case JUnit có thể được chạy tự động.
  • Test case JUnit có thể được tổ chức thành các test suite.
  • JUnit cho thấy kết quả test một cách trực quan: pass là màu xanh (hoặc tích V) và fail là màu đỏ (hoặc tích X).

3. Một số khái niệm quan trọng

  • Unit Test case: là 1 chuỗi code để đảm bảo rằng đoạn code được kiểm thử làm việc như mong đợi. Mỗi function sẽ có nhiều test case, ứng với mỗi trường hợp function chạy.
  • Setup: Đây là hàm được chạy trước khi chạy các test case, thường dùng để chuẩn bị dữ liệu để chạy test.
  • Teardown: Đây là hàm được chạy sau khi các test case chạy xong, thường dùng để xóa dữ liệu, giải phóng bộ nhớ.
  • Assert: Mỗi test case sẽ có một hoặc nhiều câu lệnh Assert, để kiểm tra tính đúng đắn của hàm.
  • Mock: là một đối tượng ảo, mô phỏng các tính chất và hành vi giống hệt như đối tượng thực được truyền vào bên trong khối mã đang vận hành nhằm kiểm tra tính đúng đắn của các hoạt động bên trong. Giả sử chương trình của chúng ta được chia làm 2 module: A và B. Module A đã code xong, B thì chưa. Để test module A, ta dùng mock để làm giả module B, không cần phải đợi tới khi module B code xong mới test được.
  • Test Suite: Test suite là một tập các test case và nó cũng có thể bao gồm nhiều test suite khác, test suite chính là tổ hợp các test.

4. Cài đặt JUnit

Hiện nay, JUnit được tích hợp sẵn trong hầu hết các Java IDE (Eclipse, NetBeans và IntelliJ). Nếu không có sẵn, chúng ta có thể tự khai báo dependency như sau:

4.1. Maven:

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

4.2 Gradle:

dependencies {
    testCompile 'junit:junit:4.12'
}

5. Ví dụ sử dụng JUnit

Giả sử chúng ta có một class util có 2 phương thức devide() và add().

  • Phương thức divide(): thực hiện chia phần nguyên của 2 số. Phương thức này nhận 2 đối số: số bị chia (dividend) và số chia (divisor). Nếu số chia là 0 thì chương trình sẽ throw một ngoại lệ, ngược lại chương trình sẽ trả về kết quả sau khi thực hiện chia nguyên.
  • Phương thức add(): sẽ thực hiện tính tổng của 2 số nguyên.

Chúng ta sẽ có một class để thực hiện bài toán trên như sau:

package company.tda.junit.util;
 
public class MathUtil {
 
    private MathUtil() {
        throw new UnsupportedOperationException("Cannot call constructor directly!");
    }
 
    public static int divide(int dividend, int divisor) {
        if (divisor == 0) {
            throw new IllegalArgumentException("Cannot divide by zero (0).");
        }
        return dividend / divisor;
    }
 
    public static int add(int number1, int number2) {
        return number1 - number2;
    }
}

Bây giờ chúng ta sẽ sử dụng JUnit để kiểm tra phương thức trên với các đầu vào khác nhau.

  • Đầu tiên chúng ta sẽ tạo một class mới với suffix là xxxTest. Đây là một naming convention cho Unit Test. Class này nên được đặt trong thư mục test, cùng package name với tên của class cần viêt unit test để dễ dàng quản lý.

  • Tiếp theo, chúng ta sẽ tạo test case để test các trường hợp có thể có của một phương thức. Mỗi test case nên tạo một phương thức để kiểm tra:

    • Phương thức devide(): có thể có một số trường hợp test sau: trường hợp kết quả phép chia ra một số nguyên, trường hợp kết quả phép chia ra số lẻ, trường hợp số chia là số 0.
    • Phương thức add(): chỉ đơn giản kiểm tra kết quả cộng 2 số.
package company.tda.junit.util;
 
import org.junit.Assert;
import org.junit.Test;
 
public class MathUtilTest {
 
    @Test
    public void divide_SixDividedByTwo_ReturnThree() {
        final int expected = 3;
 
        final int actual = MathUtil.divide(6, 2);
 
        Assert.assertEquals(expected, actual);
    }
 
    @Test
    public void divide_OneDividedByTwo_ReturnZero() {
        final int expected = 0;
 
        final int actual = MathUtil.divide(1, 2);
 
        Assert.assertEquals(expected, actual);
    }
 
    @Test(expected = IllegalArgumentException.class)
    public void divide_OneDividedByZero_ThrowsIllegalArgumentException() {
        MathUtil.divide(1, 0);
    }
 
    @Test
    public void add_SixAddedByTwo_ReturnEight() {
        final int expected = 8;
 
        final int actual = MathUtil.add(6, 2);
 
        Assert.assertEquals(expected, actual);
    }
}

Để chạy kiểm tra các test case trên, chúng ta sẽ chọn chuột phải trên class tương ứng cần test, sau đó chọn Run 'MathUtilTest'. Tương tự, chúng ta cũng có thể thực thi test cho một phương thức hoặc cả project.

Chúng ta có kết quả như sau:

Trên kết quả test, chúng ta có thể thấy được tổng thời gian thực hiện tất cả các test case (4 ms), thời gian thực thi mỗi test case, kết quả các test case tương ứng.

Như trong ví dụ trên, chúng ta có 3 phương thức pass (tích V màu xanh) cho phương thức divide(). Điều này có nghĩa là code logic của phương thức devide() đã đúng như mong đợi.

Phương thức add_SixAddedByTwo_ReturnEight() có tích X màu vàng, điều này có nghĩa là logic của phương thức add() đã có gì đó không đúng như mong đợi. Khi chúng ta click chuột vào test case bị lỗi, IDE sẽ hiển thị một vài thông tin chi tiết tại sao kết quả lại fail (log). Như hình bên trái, chúng ta có thể thấy là phương thức add() đang mong muốn là 8nhưng kết quả là 4. Từ đó, chúng ta có thể kiểm tra lại code của chương trình để tìm nguyên nhân xảy ra kết quả không mong đợi.

6. Kiểm tra độ bao phủ của Unit Test

Kiểm tra độ bao phủ của Unit Test giúp chúng ta biết được các unit test đã viết đã chạy qua các đoạn code hay chưa, nhằm đảm bảo hạn chế tối thiểu việc bỏ sót các test case và đánh giá được chất lượng của Unit Testcase, cũng như dễ dàng bổ sung testcase cho những đoạn code còn thiếu.

Nếu sử dụng IDE IntelliJ thì chúng ta có thể kiểm tra độ bao phủ như hình dưới (Nếu sử dụng IDE Eclipse, … thì có thể sử dụng plugin Jacoco):

Sau khi chạy xong, chúng ta sẽ có kết quả như hình dưới:

Kết quả sẽ có các thông tin như:

  • Phần trăm các class, method, line đã được bao phủ
  • Các dòng code nào đã được chạy qua (màu xanh)
  • Các dòng code nào chưa được chạy qua (màu đỏ)
  • Các dòng code nào chưa phủ hết các trường hợp (màu vàng) đối với các đoạn code rẽ nhánh

Tham khảo GP Coder


Tags

202103junitjava
hai.nguyen

hai.nguyen

Developer

Expertise

java

Related Posts

Garbage collector – Trình thu gom rác trong java
Articles
Garbage collector – Trình thu gom rác trong java
April 06, 2021
3 min
System operator: Những câu chuyện chưa từng được kể (Phần 1)
Others
System operator: Những câu chuyện chưa từng được kể (Phần 1)
March 31, 2021
5 min
Nâng cao bảo mật ứng dụng bằng Android NDK
Tips / Tricks
Nâng cao bảo mật ứng dụng bằng Android NDK
March 26, 2021
4 min
© 2021, All Rights Reserved.

Quick Links

HomeOur Team

Social Media