訂單金額 500 元(含)以下運費 100,超過 500 到 1000 元(含)運費 60,超過 1000 元免運。規則看起來簡單,真正容易出錯的是剛好 500、剛好 1000 那兩個金額。
改運費規則時,與其改完再靠眼睛檢查,不如先把每個金額該收多少寫下來,再讓程式一次比完。這篇就是這個做法。
先看流程
答案在程式之前就定好。之後不管怎麼改程式,重跑一次,就知道哪幾筆壞了。
先準備與執行
- 在試算表選「擴充功能 → Apps Script」,把三個區塊依序貼進去並儲存。
- 在上方函式選單選
runShippingTests,按「執行」。若 Google 要求授權,先看清楚列出的權限再同意。 - 程式會自己建立「測試結果」分頁;「執行記錄」會顯示「通過 12/12」。
區塊一:運費規則與函式
/**
* @OnlyCurrentDoc
*/
// 訂單金額 ≤500 運費 100;501 到 1000 運費 60;超過 1000 免運
const FEE_TABLE = [
{ max: 500, fee: 100 },
{ max: 1000, fee: 60 },
{ max: Infinity, fee: 0 },
];
// 回傳運費;金額無法判斷(空白、負數、不是數字)時回傳 null,不當成免運
function shippingFee(amount) {
const isText = typeof amount === "string" && amount.trim() !== "";
const n = typeof amount === "number" ? amount : isText ? Number(amount) : NaN;
if (!Number.isFinite(n) || n < 0) {
return null;
}
return FEE_TABLE.find((t) => n <= t.max).fee;
}- 最上面的
@OnlyCurrentDoc註解,宣告這支程式只會用到目前這份試算表。 FEE_TABLE由小到大排列,函式取第一個「金額 ≤ 上限」的級距。<=就是決定「剛好 500 算哪一邊」的地方。- 金額無法判斷時(空白、負數、不是數字)回傳
null,不是 0。回 0 等於「免運」,錯誤資料就會被默默當成免運。 - 儲存格裡的
"500"可能是文字,函式先轉成數字再比。
區塊二:先定答案的測試案例
// 先定答案:每一筆都是「輸入」與「預期」,邊界的兩側都要有
const TEST_CASES = [
{ amount: 0, expected: 100 },
{ amount: 1, expected: 100 },
{ amount: 500, expected: 100 },
{ amount: 500.5, expected: 60 },
{ amount: 501, expected: 60 },
{ amount: 1000, expected: 60 },
{ amount: 1000.01, expected: 0 },
{ amount: 1001, expected: 0 },
{ amount: "500", expected: 100 },
{ amount: "", expected: null },
{ amount: -1, expected: null },
{ amount: "abc", expected: null },
];- 每一筆都是「輸入」加「預期」。邊界的兩側都要有:500 與 501、1000 與 1001。
- 500.5 和 1000.01 檢查「剛過邊界」的小數:規則是「≤500 才是 100」,所以 500.5 算 60。
"500"(文字)、""(空白)、-1、"abc"檢查輸入不是正常數字時的反應。- 0 元被當成最低級距、運費 100,這是這篇的決定;你的規則可能不同,就改預期。
區塊三:執行並寫成報告
function runShippingTests() {
const book = SpreadsheetApp.getActiveSpreadsheet();
const sheet = book.getSheetByName("測試結果") || book.insertSheet("測試結果");
sheet.clearContents();
const show = (value) => (typeof value === "string" ? '"' + value + '"' : String(value));
let passed = 0;
const rows = TEST_CASES.map((testCase) => {
const actual = shippingFee(testCase.amount);
const ok = actual === testCase.expected;
if (ok) {
passed++;
}
return [show(testCase.amount), String(testCase.expected), String(actual), ok ? "PASS" : "FAIL"];
});
sheet.getRange(1, 1, rows.length + 1, 4).setValues([["輸入", "預期", "實際", "結果"]].concat(rows));
Logger.log("通過 " + passed + "/" + rows.length);
}- 每一筆呼叫一次
shippingFee,和預期比對,寫成PASS或FAIL。 - 結果整張一次
setValues寫入,不是逐格寫。 - 每次執行先
clearContents(),可以重跑;也因此「測試結果」分頁會整張被覆寫,不要在上面放別的東西。
同一條規則,用公式寫一次
函式要驗證的規則,用公式也寫得出來。下表可以改金額,看公式算出的運費:
| 列 / 欄 | A | B | C |
|---|---|---|---|
| 1 | 金額 | 公式算出的運費 | |
| 2 | 0 | (公式 =IF(A2<=500,100,IF(A2<=1000,60,0)),尚未計算) | |
| 3 | 1 | (公式 =IF(A3<=500,100,IF(A3<=1000,60,0)),尚未計算) | |
| 4 | 500 | (公式 =IF(A4<=500,100,IF(A4<=1000,60,0)),尚未計算) | |
| 5 | 500.5 | (公式 =IF(A5<=500,100,IF(A5<=1000,60,0)),尚未計算) | |
| 6 | 501 | (公式 =IF(A6<=500,100,IF(A6<=1000,60,0)),尚未計算) | |
| 7 | 1000 | (公式 =IF(A7<=500,100,IF(A7<=1000,60,0)),尚未計算) | |
| 8 | 1000.01 | (公式 =IF(A8<=500,100,IF(A8<=1000,60,0)),尚未計算) | |
| 9 | 1001 | (公式 =IF(A9<=500,100,IF(A9<=1000,60,0)),尚未計算) | |
| 10 | |||
| 11 |
試試看!修改數字,公式會自動重算 • 重新整理即還原
公式適合在表格裡直接算;函式適合被別的程式呼叫,也能用同一份案例反覆驗證。
故意改錯,看測試抓不抓得到
把 FEE_TABLE.find 裡的 n <= t.max 改成 n < t.max,只差一個等號。重跑後執行記錄變成「通過 9/12」,「測試結果」分頁是:
| 列 / 欄 | A | B | C | D |
|---|---|---|---|---|
| 1 | 輸入 | 預期 | 實際 | 結果 |
| 2 | 0 | 100 | 100 | PASS |
| 3 | 1 | 100 | 100 | PASS |
| 4 | 500 | 100 | 60 | FAIL |
| 5 | 500.5 | 60 | 60 | PASS |
| 6 | 501 | 60 | 60 | PASS |
| 7 | 1000 | 60 | 0 | FAIL |
| 8 | 1000.01 | 0 | 0 | PASS |
| 9 | 1001 | 0 | 0 | PASS |
| 10 | "500" | 100 | 60 | FAIL |
| 11 | "" | null | null | PASS |
| 12 | -1 | null | null | PASS |
| 13 | "abc" | null | null | PASS |
示範試算表
錯的剛好是 500、1000 與 "500",邊界案例立刻現形。這個表是預先寫好的結果,不會自己執行程式。
換成你的情境
- 級距與運費:
FEE_TABLE的max、fee。 - 邊界算哪一邊:
<=或<;改了,預期值也要跟著改。 - 無效金額怎麼處理:回傳
null、改成報錯,或當成 0,由你的業務規則決定。 - 測試案例:
TEST_CASES,每個級距邊界的兩側各一筆。
多一個運費級距,案例怎麼改?
規則改成:500 元以下 100、501 到 1000 元 60、1001 到 2000 元 30、超過 2000 元免運。先預測:FEE_TABLE 要怎麼改?哪幾筆既有案例的預期會變?要新增哪幾筆?
延伸閱讀
接下來可以
相關文章
查看所有文章儲存格的值與顯示格式:看起來一樣,其實不一樣
儲存格存的值和畫面上看到的樣子是兩件事。用圖看日期、百分比、小數位數、帶單位的數字,學會用 N、ISNUMBER 查出真相,也知道格式該設在哪裡。
會員分級與代碼對照:IFS、SWITCH 還是查表?
用一份會員消費資料,示範 IFS 做會員分級、SWITCH 把付款代碼轉成中文,並說明門市代碼變多、門檻會調整的時候,為什麼改成對照表加 XLOOKUP 比較好維護。
Apps Script 批次讀寫:一次讀、整理、一次寫回客戶名單
以客戶名單示範:一次讀成二維陣列,整理姓名空白與電話格式,再一次寫回指定範圍。附完整程式、前後對照表,以及 2000 列的耗時對照。
留言討論
共 0 則留言
