在講解 JBuilder 2005 單元測試功能前,先建立兩個典型業務類別作為測試標的:分段函數類別與字串處理工具類別。這兩個類別涵蓋邊界條件判斷、例外處理、集合操作等常見業務邏輯,適合示範測試案例設計技巧。
SubsectiongetValue(int d) 依輸入值 d 計算結果:
d < -2:回傳 |d|(絕對值)-2 ≤ d < 2 且 d ≠ 0:回傳 d²d = 0:回傳 100(特殊業務常數)d ≥ 2:回傳 d³sign(double d):符號函數,負數回傳 -1,正數回傳 1,零回傳 0。
package chapter25;
public class Subsection {
public static int getValue(int d) {
if (d == 0) {
return 100;
} else if (d < -2) {
return Math.abs(d);
} else if (d >= -2 && d < 2) {
return d * d;
} else { // d >= 2
// 潛在 Bug:d > 32 時 d*d*d 會超過 Integer.MAX_VALUE (2,147,483,647)
// 原開發者曾嘗試加入防護但被註解掉
// if (d > 32) return Integer.MAX_VALUE;
return d * d * d;
}
}
public static int sign(double d) {
if (d < 0) return -1;
else if (d > 0) return 1;
else return 0;
}
}
整數溢位風險:int 最大值約 21 億,d³ 當 d ≥ 1290 即溢位(1290³ = 2,146,689,000,1291³ = 2,151,685,571 超過)。原碼註解錯誤標示 32 為臨界值(實為 32³=32768 遠小於上限),顯示開發者對資料型別範圍認知不清。測試案例必須覆蓋 d = 1290, 1291, 2000 等邊界值。
StringUtilsstring2Array()將字串依分隔字元切割為陣列,支援修剪首尾分隔字元選項。
package chapter25;
public class StringUtils {
/** 對外公開 API */
public static String[] string2Array(String str, char splitChar, boolean trim) {
if (str == null) return null;
String tempStr = str;
if (trim) tempStr = trim(tempStr, splitChar); // 移除首尾分隔字元
int arraySize = getCharCount(tempStr, splitChar) + 1;
String[] result = new String[arraySize];
int from = 0, end = 0;
for (int i = 0; i < result.length; i++) {
end = tempStr.indexOf(splitChar, from);
if (end == -1) { // 最後一段
result[i] = tempStr.substring(from);
break;
}
result[i] = tempStr.substring(from, end);
from = end + 1;
}
return result;
}
/** 移除字串首尾指定分隔字元 */
private static String trim(String str, char splitChar) {
int begin = 0, end = str.length();
// 尋找首個非分隔字元
for (int i = 0; i < str.length(); i++) {
if (str.charAt(i) != splitChar) { begin = i; break; }
}
// 尋找尾部首個非分隔字元(反向)
for (int i = str.length(); i > 0; i--) {
if (str.charAt(i - 1) != splitChar) { end = i; break; }
}
return str.substring(begin, end);
}
/** 計算分隔字元出現次數 */
private static int getCharCount(String str, char splitChar) {
int count = 0;
for (int i = 0; i < str.length(); i++)
if (str.charAt(i) == splitChar) count++;
return count;
}
}
| 場景 | 輸入範例 | 預期行為 | 風險點 |
|---|---|---|---|
| Null 輸入 | null | 回傳 null | NullPointerException |
| 空字串 | "" | 回傳長度 1 的陣列 [""] | 邊界條件 |
| 首尾分隔字元 | ",a,b," (trim=true) | ["a", "b"] | trim 邏輯正確性 |
| 連續分隔字元 | "a,,b" | ["a", "", "b"] | 空元素保留 |
| 無分隔字元 | "abc" | ["abc"] | 單元素陣列 |
| 全分隔字元 | ",,," (trim=true) | [""] 或 [] | 極端邊界 |
| Unicode 分隔字元 | "一、二、三" ('、') | 正確切割 | char 處理正確性 |
針對 Subsection.getValue() 設計測試資料:
public class SubsectionTest extends TestCase {
// 等價類:d < -2
public void testNegativeLessThanMinus2() {
assertEquals(5, Subsection.getValue(-5));
assertEquals(3, Subsection.getValue(-3));
}
// 邊界:-2
public void testBoundaryMinus2() {
assertEquals(4, Subsection.getValue(-2)); // (-2)^2 = 4
}
// 等價類:-2 < d < 0
public void testNegativeBetween() {
assertEquals(1, Subsection.getValue(-1));
}
// 特殊值:0
public void testZero() {
assertEquals(100, Subsection.getValue(0));
}
// 等價類:0 < d < 2
public void testPositiveLessThan2() {
assertEquals(1, Subsection.getValue(1));
}
// 邊界:2
public void testBoundary2() {
assertEquals(8, Subsection.getValue(2)); // 2^3 = 8
}
// 整數溢位邊界(關鍵缺陷暴露)
public void testOverflowBoundary() {
assertEquals(2146689000, Subsection.getValue(1290)); // 正常
// 1291 會溢位產生負數或錯誤值,測試可驗證實際行為
int result = Subsection.getValue(1291);
System.out.println("d=1291 result=" + result); // 觀察溢位結果
// 若需修正,應在產品碼加入檢查或改用 long
}
}
// StringUtils 測試資料表
@ParameterizedTest
@CsvSource({
"null, , , true, null",
"'', , , false, '[\"\"]'",
"'a,b,c', ',', false, '[\"a\",\"b\",\"c\"]'",
"'a,,b', ',', false, '[\"a\",\"\",\"b\"]'",
"',a,b,', ',', true, '[\"a\",\"b\"]'",
"'、', '、', true, '[\"\"]'",
})
void testString2Array(String input, char split, boolean trim, String expected) {
// 解析 expected JSON 陣列與實際比對
}
test*)。透過 Subsection(數學邊界/溢位)與 StringUtils(字串切割/空值/Unicode)兩個典型業務類別,展示了單元測試案例設計的核心技巧:等價類劃分、邊界值分析、極端場景覆蓋、缺陷導向測試。JBuilder 2005 整合 JUnit 提供視覺化測試執行環境,配合測試先行(TDD)流程,能在開發早期攔截如整數溢位、空指標、邊界邏輯錯誤等高風險缺陷,大幅降低後期修復成本。