VieTopik
Tiếng HànTiếng AnhIT
  • Góc học tập
Tải app
  • Thư viện
  • Luyện thi
  • Cẩm nang
  • Góc học tập
Kiến trúc và chất lượng codeTest
Bài 7/17
5 phút

Test đáng tin

Nội dung bài · 5 mục
  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt

Một test hôm qua còn xanh, hôm nay lại đỏ, dù không ai sửa code. Chạy lại thì xanh. Test như vậy làm cả nhóm mất lòng tin: thấy đỏ mà không biết là code lỗi thật hay do chính test. Bài này chỉ ra các nguyên nhân hay gặp và cách tránh.

Khái niệm

🕰️ Test đáng tin (deterministic test): test cho cùng một kết quả mỗi lần chạy, bất kể chạy ngày nào, trên máy nào, trước hay sau test khác.

Ba nguyên nhân hay gặp nhất khiến test không đáng tin: phụ thuộc giờ hệ thống như LocalDate.now(), phụ thuộc thứ tự chạy của các test, và phụ thuộc database hay mạng (bài trước đã thay bằng fake).

Ví dụ

Giảm 10% vào thứ bảy, chủ nhật. Method nhận ngày qua tham số thay vì tự đọc giờ hệ thống:

// Promo.java
package com.shop.api;

import java.time.*;

class Promo {
    public int discountPercent(LocalDate day) {
        DayOfWeek dow = day.getDayOfWeek();
        if (dow == DayOfWeek.SATURDAY
                || dow == DayOfWeek.SUNDAY) {
            return 10;
        }
        return 0;
    }
}

// PromoTests.java
package com.shop.api;

import static org.junit.jupiter.api.Assertions.*;

import java.time.*;
import org.junit.jupiter.params.*;
import org.junit.jupiter.params.provider.*;

class PromoTests {
    @ParameterizedTest
    @CsvSource({
        "2026-09-26, 10",  // thứ bảy
        "2026-09-28, 0"    // thứ hai
    })
    public void discountPercentByDay(
            LocalDate day, int expected) {
        var promo = new Promo();

        int percent = promo.discountPercent(day);

        assertEquals(expected, percent);
    }
}
  • LocalDate trong gói java.time là một ngày, không kèm giờ. getDayOfWeek() cho biết ngày đó là thứ mấy.
  • Test tự chọn ngày cố định, nên chạy hôm nào cũng cho cùng kết quả.
  • JUnit tự đổi chuỗi 2026-09-26 thành LocalDate cho tham số day, nên viết ngày thẳng trong @CsvSource được.

Thử ngay

JUnit không chạy test theo thứ tự viết trong file. Thêm class này rồi chạy ./mvnw test:

// CounterTests.java
package com.shop.api;

import static org.junit.jupiter.api.Assertions.*;

import org.junit.jupiter.api.*;

class CounterTests {
    private int count = 0;

    @Test
    public void first() {
        count++;
        assertEquals(1, count);
    }

    @Test
    public void second() {
        count++;
        assertEquals(1, count);
    }
}

Đoán trước khi chạy: hai test cùng tăng count. Test chạy sau sẽ thấy count bằng 2 và đỏ, hay cả hai đều xanh?

Xem kết quả
[INFO] Running com.shop.api.CounterTests
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.099 s -- in com.shop.api.CounterTests

Cả hai xanh. JUnit tạo một object CounterTests mới cho mỗi test, nên mỗi test có count riêng, bắt đầu từ 0. Nhờ vậy các test không ảnh hưởng nhau qua field.

Đổi count thành static thì hai test dùng chung một biến, và test chạy sau sẽ đỏ.

Lỗi hay gặp

Đọc LocalDate.now() bên trong method cần test. Test chỉ xanh vào cuối tuần, ngày thường thì đỏ, dù code không đổi.

// SAI — kết quả phụ thuộc hôm nay là thứ mấy
// Promo.java
package com.shop.api;

import java.time.*;

class Promo {
    public int discountPercent() {
        DayOfWeek today =
            LocalDate.now().getDayOfWeek();
        if (today == DayOfWeek.SATURDAY
                || today == DayOfWeek.SUNDAY) {
            return 10;
        }
        return 0;
    }
}
// ĐÚNG — nhận ngày qua tham số
// Promo.java
package com.shop.api;

import java.time.*;

class Promo {
    public int discountPercent(LocalDate day) {
        DayOfWeek dow = day.getDayOfWeek();
        if (dow == DayOfWeek.SATURDAY
                || dow == DayOfWeek.SUNDAY) {
            return 10;
        }
        return 0;
    }
}

Tóm tắt

  • Test đáng tin cho cùng kết quả mỗi lần chạy.
  • Không đọc LocalDate.now() trong code cần test, truyền ngày vào qua tham số.
  • JUnit tạo object test mới cho mỗi test. Tránh field static dùng chung giữa các test.
  • Phụ thuộc database hay mạng thì thay bằng fake.

Tự kiểm tra

0/3 câu
Câu 1

Test tính phí giao hàng đêm khuya chạy lúc 23 giờ thì xanh, lúc 9 giờ sáng thì đỏ. Nguyên nhân nhiều khả năng nhất?

Câu 2

Hai test trong cùng class cùng thêm phần tử vào một field List (không static). Test thứ hai có thấy phần tử của test thứ nhất không?

Câu 3

Test đọc sản phẩm từ Oracle thật. Hôm qua xanh, hôm nay đỏ vì có người xoá dữ liệu. Sửa thế nào?

Fake thay phụ thuộcĐặt tên và method ngắn

Nội dung bài

  1. 1.Khái niệm
  2. 2.Ví dụ
  3. 3.Thử ngay
  4. 4.Lỗi hay gặp
  5. 5.Tóm tắt