iT邦幫忙

2026 iThome 鐵人賽

0
ChatGPT & Codex

從 Prompt Engineering 到 Copilot / Codex 實務 30 天系列 第 25 篇

Day 25:【測試覆蓋率】讓 AI 主動挖掘邊界案例(Boundary Values)以補全測試盲點

  • 分享至 

  • xImage
  •  

昨天我們學會了如何透過 JUnit 5 與 Mockito 快速產出單元測試框架。然而在軟體工程中,很多人常陷入一種「高覆蓋率的假象」:行覆蓋率(Line Coverage)看似達到了 90%,但一到線上環境,使用者傳入奇怪的字元、邊界數值或特定時間戳時,系統依然瞬間崩潰。

原因在於:開發者自己寫測試時,往往存在思維慣性,只會測試「自己預想得到的情境」。

在軟體測試理論中,最容易爆出 Bug 的地方往往集中在**「邊界(Boundaries)」**——例如:0 與 1 之間、集合為空或剛好滿載、整數最大值(Integer.MAX_VALUE)、以及日期閏年切換。

今天我們就來示範:如何讓 ChatGPT 扮演挑剔的「QA 邊界破壞者」,丟入一段既有代碼,讓 AI 主動揪出隱藏的邊界盲點,並透過 JUnit 5 的**參數化測試(@ParameterizedTest)**一口氣補全測試防線!


一、邊界案例挖掘專用提示詞模板(Boundary Mining Prompting)

想要讓 AI 幫你找出思考盲區,可以使用以下「逆向測試」提問架構:

【邊界案例挖掘提示詞範本】
請扮演資深 QA 自動化測試架構師與軟體測試黑客。以下是一段已經撰寫完成的業務邏輯代碼:

[貼上既有的業務方法]

請依序進行邊界分析與補全:

  1. 邊界盲區盤點(Boundary Analysis):
    • 數值邊界:0、負數、極值溢位(MIN/MAX)、浮點數精度。
    • 集合與字串邊界:null、空字串、純空白、超長字串、包含特殊字元/Emoji。
    • 商業邏輯邊界:剛好達標(如剛好滿 1000 元打折)、剛好差 1 元、跨日/跨月/閏年。
  2. 脆弱點警告:指出這段代碼在面對上述哪些邊界時,會產生未預期的錯誤或拋出 RuntimeException?
  3. JUnit 5 參數化測試補全:使用 @ParameterizedTest 與 @CsvSource / @MethodSource 撰寫高密度的邊界測試案例,一次覆蓋所有極端情境。

二、實戰案例:VIP 會員生日禮金與折扣計算器

假設我們有一個根據消費金額與是否為生日月份計算折扣的業務方法:

[待測商業邏輯代碼]
public class LoyaltyDiscountCalculator {

    /**
     * 計算折抵後的最終金額
     * @param totalAmount 原始金額
     * @param isBirthday 是否為生日當月
     * @param memberPoints 現有紅利點數 (每 10 點可折抵 1 元,最高折抵金額的 20%)
     */
    public double calculateFinalPrice(double totalAmount, boolean isBirthday, int memberPoints) {
        if (totalAmount <= 0) {
            return 0.0;
        }

        // 滿千送百
        double discount = Math.floor(totalAmount / 1000) * 100;

        // 生日當月額外享 9 折
        double discountedPrice = totalAmount - discount;
        if (isBirthday) {
            discountedPrice = discountedPrice * 0.9;
        }

        // 點數折抵:最高折抵當前金額 20%
        double maxPointDeduction = discountedPrice * 0.2;
        double actualPointDeduction = Math.min(memberPoints / 10.0, maxPointDeduction);

        return discountedPrice - actualPointDeduction;
    }
}

三、AI 指出的隱蔽邊界盲區

當我們請 AI 進行邊界審查時,它列出了開發者極易忽略的三大漏洞:

  1. 浮點數精度遺失(Floating Point Precision):
    double 進行乘法與減法運算時(如 * 0.9),容易產生 199.99999999999997 的精度誤差,在金融與計價場景應全面換成 BigDecimal 或做精確四捨五入。
  2. 負點數或溢位攻擊:
    若傳入的 memberPoints 為負數(例如 -50),Math.min(-5.0, maxPointDeduction) 會得出負值,反而讓消費者的折抵金額「變成加價」!
  3. 剛好臨界點的行為驗證:
    金額剛好為 999.99、1000.00、1000.01 時的折抵表現是否符合業務規範?

四、AI 生成的 JUnit 5 參數化邊界測試

AI 推薦使用 @ParameterizedTest 搭配 @CsvSource,用一張清晰的測試矩陣一次性覆蓋正常與極端邊界:

[JUnit 5 參數化測試實作]
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.within;

class LoyaltyDiscountCalculatorTest {

    private final LoyaltyDiscountCalculator calculator = new LoyaltyDiscountCalculator();

    @ParameterizedTest(name = "[{index}] 金額={0}, 生日={1}, 點數={2} => 預期最終價={3}")
    @CsvSource({
        // --- 1. 金額邊界測試 ---
        "0.0,    false, 0,    0.0",      // 金額為 0
        "-100.0, false, 0,    0.0",      // 負數金額防呆
        "999.0,  false, 0,    999.0",    // 剛好未達滿千門檻
        "1000.0, false, 0,    900.0",    // 剛好達到滿千折百 (1000 - 100)
        "1000.1, false, 0,    900.1",    // 剛好超過滿千

        // --- 2. 生日折扣邊界 ---
        "1000.0, true,  0,    810.0",    // 滿千折百後再打 9 折: (1000 - 100) * 0.9 = 810.0

        // --- 3. 點數折抵上限邊界 (最高折抵 20%) ---
        "1000.0, false, 500,  850.0",    // 900 元,上限 20% 為 180 元。傳入 500 點可折 50 元 (未達上限)
        "1000.0, false, 2000, 720.0",    // 傳入 2000 點可折 200 元,但觸碰上限 180 元,應只折 180 (900 - 180 = 720)
        "1000.0, false, 0,    900.0"     // 0 點數情況
    })
    @DisplayName("驗證各類金額、生日優惠與點數折抵之邊界案例")
    void calculateFinalPrice_BoundaryCases(double totalAmount, boolean isBirthday, int memberPoints, double expectedPrice) {
        // [Act]
        double actualPrice = calculator.calculateFinalPrice(totalAmount, isBirthday, memberPoints);

        // [Assert] 使用 within 容許微小的雙精度浮點誤差
        assertThat(actualPrice).isCloseTo(expectedPrice, within(0.001));
    }
}

五、工程師在測試覆蓋率上的體會

  1. 參數化測試是提升覆蓋率的神器:
    過去要測 8 種情境得寫 8 個 @Test 方法,代碼冗長且難維護;透過 @ParameterizedTest,只需維護一張簡潔的 CSV 測試矩陣,幾行代碼就能涵蓋十幾種邊界情況。
  2. AI 是最佳的「思考盲點鏡子」:
    自己在寫邏輯時,大腦會自動繞過那些沒考慮到的極端值;但把代碼丟給 AI 進行「破壞性審查」,它能站在無偏見的角度提出「負數、上限極值、0 點數」等情境,替生產環境擋下未爆彈。

今日小結與預告

今天我們掌握了利用 AI 挖掘程式碼邊界盲區的方法,並透過 JUnit 5 參數化測試架構建立了高密度的防禦測試網。


上一篇
Day 24:【單元測試生成】用 AI 快速撰寫 JUnit 5 與 Mockito 測試案例:輕鬆把測試覆蓋率拉滿
下一篇
Day 26:【資安防護一】AI 提示詞防禦:認識 Prompt Injection(提示詞注入)與防護實務
系列文
從 Prompt Engineering 到 Copilot / Codex 實務 30 天 共 29 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言