Tìm hiểu nhanh JUnit 4

1. JUnit là gì?

Có khá nhiều tool/framework để thực hiện kiểm thử đơn vị. Với ngôn ngữ Java, chúng ta có 2 framework hay dùng nhất là JUnit và TestNG.

JUnit là một framework đơn giản và mã nguồn mở, với các tính năng nổi bật như:

  • Assertion: dùng để xác nhận kết quả mong muốn
  • Test fixture: dùng để chia sẻ dữ liệu test dùng chung
  • Test runner: dùng để chạy các test

Phiên bản hiện tại của JUnit là JUnit 5. Tuy nhiên, trong tutorial này, mình sẽ hướng dẫn các bạn với JUnit 4 nhé. Đây vẫn là một phiên bản JUnit được sử dụng phổ biến.


2. JUnit setup

Để sử dụng JUnit trong Maven project, chúng ta cần import dependency junit:

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>

Trong Maven project, chúng ta có thể thấy trong thư mục src có 2 thư mục con là:

  • main/java: thư mục này chứa các lớp code của ứng dụng.
  • test/java: thư mục này chứa các lớp test của ứng dụng.

Trong src/main/java, chúng ta tạo một lớp Calculator để thực hiện cộng, trừ, nhân, chia 2 số nguyên:

Calculator.java

package org.ashina.tutorial.junit;

public class Calculator {

    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }

    public int multiply(int a, int b) {
        return a * b;
    }

    public int divide(int a, int b) {
        return a / b;
    }
    
}

Trong src/test/java, chúng ta tạo một lớp CalculatorTest để test lớp Calculator ở trên:

CalculatorTest.java

package org.ashina.tutorial.junit;

import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {

    @Test
    public void add() {
        Calculator calculator = new Calculator();
        int expected = 3;
        int actual = calculator.add(1, 2);
        assertEquals(expected, actual);
    }

}

Trong ví dụ này, mình chỉ test phương thức add() mà thôi.

Để chạy unit test trong Maven project, chúng ta có các lệnh hay dùng sau:

# Chạy tất cả các lớp unit test
$ mvn test

# Chạy một lớp unit test
$ mvn -Dtest=FooTest test

# Chạy nhiều lớp unit test
$ mvn -Dtest=FooTest,BarTest test

# Chạy một phương thức trong một lớp unit test
$ mvn -Dtest=FooTest#foo test

Đây là kết quả ở console sau khi chạy lệnh mvn test:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.ashina.tutorial.junit.CalculatorTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.292 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

Chú ý: Với các IDE phổ biến như Eclipse, Netbeans, Intellij IDEA, các bạn hoàn toàn có thể chạy thẳng unit test mà không cần phải dùng lệnh của Maven như trên.


3. JUnit Annotations

Dưới đây là các JUnit annotation hay dùng:

Annotation Mô tả
@Test Mỗi một lớp test sẽ bao gồm nhiều phương thức test được đánh dấu bởi annotation @Test. Mỗi phương thức test này tương ứng một test case.
@Test(exception = MyException.class) Test case sẽ fail nếu như code không bắn ra ngoại lệ MyException.
@Test(timeout = 1000) Test case sẽ fail nếu như code được thực thi quá 1000 millisecond.
@Before Phương thức @Before được thực thi trước mỗi phương thức @Test, được dùng để chuẩn bị dữ liệu test.
@After Phương thức @After được thực thi sau mỗi phương thức @Test, được dùng để dọn dẹp dữ liệu test.
@BeforeClass Phương thức @BeforeClass được thực thi một lần duy nhất, trước tất cả các phương thức @Before@Test, và phải có modifier static.
@AfterClass Phương thức @AfterClass được thực thi một lần duy nhất, sau khi tất cả các phương thức @Test@After hoàn thành, và phải có modifier static.
@Ignore Annotation này dùng để disable một phương thức @Test.

Trong ví dụ sau, mình sẽ phát triển thêm CalculatorTest trong bài viết trước: có tất cả 4 test case, trong đó 1 test case sẽ bị disable.

CalculatorTest.java

package org.ashina.tutorial.junit;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.*;

public class CalculatorTest {

    private static Calculator calculator;

    @BeforeClass
    public static void setUpClass() {
        calculator = new Calculator();
        System.out.println("@BeforeClass");
    }

    @Before
    public void setUp() {
        System.out.println("@Before");
    }

    @After
    public void tearDown() {
        System.out.println("@After");
    }

    @AfterClass
    public static void tearDownClass() {
        System.out.println("@AfterClass");
    }

    @Test
    public void add() {
        int expected = 3;
        int actual = calculator.add(1, 2);
        assertEquals(expected, actual);
    }

    @Test(expected = ArithmeticException.class)
    public void divide1() {
        calculator.divide(1, 0);
    }

    @Ignore
    @Test
    public void divide2() {
        calculator.divide(2, 0);
    }

}

Kết quả:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.ashina.tutorial.junit.CalculatorTest
@BeforeClass
@Before
@After
@Before
@After
@AfterClass
Tests run: 3, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.296 sec

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 1

Chú ý: Những phương thức được đánh dấu bởi @BeforeClass@AfterClass phải là static.


4. JUnit Assertion

Package org.junit.Assert cung cấp các phương thức static để so sánh kết quả mong muốn (expected result) với kết quả thực tế (actual result). Nếu việc so sánh bị fail, ngoại lệ AssertionException sẽ được ném ra.

So sánh boolean

import static org.junit.Assert.*;

@Test
public void testBoolean() {
    int a = 2;
    int b = 1;
    assertTrue(a > b);
    assertFalse(a <= b);
}

So sánh null

@Test
public void testNull() {
    String str1 = null;
    String str2 = "abc";
    assertNull(str1);
    assertNotNull(str2);
}

So sánh đối tượng

So sánh đối tượng dựa trên toán tử == (so sánh dựa trên tham chiếu):

@Test
public void testSame() {
    String str1 = "abc";
    String str2 = "abc";
    String str3 = new String("abc");
    assertSame(str1, str2);
    assertNotSame(str2, str3);
}

So sánh đối tượng dựa trên phương thức equals():

@Test
public void testEquals() {
    String str1 = "abc";
    String str2 = "abc";
    String str3 = "def";
    assertEquals(str1, str2);
    assertNotEquals(str2, str3);
}

So sánh mảng

@Test
public void testArrayEquals() {
    int[] arr1 = new int[]{1, 2, 3};
    int[] arr2 = new int[]{1, 2, 3};
    assertArrayEquals(arr1, arr2);
}

5. JUnit Parameterized

JUnit 4 giới thiệu một tính năng mới khá hay là parameterized test. Parameterized test cho phép lập trình viên có thể chạy một phương thức test nhiều lần với các bộ tham số đầu vào có giá trị khác nhau.

Mình sẽ thực hiện test phương thức add() của lớp Calculator như sau:

package org.ashina.tutorial.junit;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.util.Arrays;
import java.util.List;

import static org.junit.Assert.assertEquals;

@RunWith(Parameterized.class)
public class CalculatorTest {

    private static Calculator calculator;

    @Parameterized.Parameter(0)
    public int num1;
    @Parameterized.Parameter(1)
    public int num2;
    @Parameterized.Parameter(2)
    public int result;

    @BeforeClass
    public static void setUpClass() {
        calculator = new Calculator();
    }

    @Parameterized.Parameters
    public static List<Object[]> createTestData() {
        Object[][] data = new Object[][] { {1, 1, 2}, {1, 2, 3}, {2, 1, 3} };
        return Arrays.asList(data);
    }

    @Test
    public void add() {
        assertEquals(result, calculator.add(num1, num2));
    }

}

Chú ý: Thứ tự các giá trị trong mỗi bộ tham số sẽ tương ứng với thứ tự của các thuộc tính @Parameterized.Parameter.

Kết quả:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.ashina.tutorial.junit.CalculatorTest
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.213 sec

Results :

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0

Tài liệu tham khảo

http://www.vogella.com/tutorials/JUnit/article.html