TDD(測試驅動開發)

什麼是 TDD?

是一種開發流程,觀念是「先寫測試,在進入開發工作」。在進行開發工作以前,編寫測試,預先模擬欲測試的情境。

為什麼要寫 TDD?

預防發生錯誤能馬上知道在哪?

1.測試本身就是規格

2.寫出更有信心的程式碼

3.可以做出比較好的設計

4.將來有重構的可能性

如何寫 TDD?

  1. 添加測試
    • 增加新功能首先要編寫一個測試,如果滿足該功能的規範則通過該測試。
    • 可以通過詢問來發現這些規範。
    • 測試驅動開發的一個關鍵好處是它使開發人員在編寫代碼之前專注於需求。
  2. 運行所有測試。預期新測試應該失敗(紅燈)
    • 表明所需的功能實際上需要新代碼。
    • 只會有一件事最多兩件事,不要讓測試複雜
    • 驗證測試工具是否正常工作。
    • 排除了新測試存在缺陷並始終通過的可能性。
  3. 編寫通過新測試的最簡單的代碼(綠燈)
    • 不優雅或硬代碼是可以接受的,只要它通過了測試。
  4. 現在所有測試都應該通過了
    • 如果有任何失敗,則必須修改新代碼,直到它們通過。
    • 這可確保新代碼滿足測試要求並且不會破壞現有功能。
  5. 根據需要進行重構,在每次重構後使用測試來確保保留功能
    • 代碼被重構以提高可讀性和可維護性。
    • 優化程式碼,包含產品程式和測試程式(測試程式也是專案需維護的一部份)。
    • 同時確保每次修改後,執行測試皆能通過。

每個新功能重複 1~3 動作,到最後 4 的所有功能測試都通過就是 TDD

範例

首先我對這個功能有以下的規範:

存錢功能:

  1. 可以存錢
  2. 不可以存 0 元或是小於 0 元的金額(越存錢越少!)

領錢功能:

  1. 可以領錢
  2. 不能領 0 元或是小於 0 元的金額(越領錢越多!)
  3. 不能領超過本身餘額

1.添加測試:首先我對可以存錢功能,只有開新帳戶有初始錢還有我存錢的錢,以及我餘額會有多少

test("可以存錢", () => {
  const account = new BankAccount(5);
  account.deposit(10);
  expect(account.balance).toBe(15);
});

2.運行所有測試。預期新測試應該失敗(紅燈)

3.編寫通過新測試的最簡單的代碼(綠燈):以我測試想要的結果寫出有 constructor 帶進我開戶有的初始值,以及 deposit 存錢的函示,還有 balance 餘額的函示

class BankAccount {
  //初始化帶值要用constructor
  constructor(accountBalance) {
    this.accountBalance = accountBalance;
  }
  deposit(money) {
    this.accountBalance += money;
  }
  //因為要的是function的回傳值,而不是function本身,所以要有get
  get balance() {
    return this.accountBalance;
  }
}

接者重複 1~3 來做餘下的功能

test("不可以存 0 元或是小於 0 元的金額(越存錢越少!)", () => {
  const account = new BankAccount(5);
  account.deposit(-10);
  expect(account.balance).toBe(5);
});
class BankAccount {
  constructor(accountBalance) {
    this.accountBalance = accountBalance;
  }
  deposit(money) {
    if (money > 0) {
      this.accountBalance += money;
    }
  }
  get balance() {
    return this.accountBalance;
  }
}

test("領錢功能", () => {
  const account = new BankAccount(20);
  const money = account.withDraw(5);
  expect(account.balance).toBe(15);
  expect(money).toBe(5);
});
class BankAccount {
  constructor(accountBalance) {
    this.accountBalance = accountBalance;
  }
  deposit(money) {
    if (money > 0) {
      this.accountBalance += money;
    }
  }
  withDraw(money) {
    this.accountBalance -= money;
    return money;
  }
  get balance() {
    return this.accountBalance;
  }
}

test("不能領 0 元或是小於 0 元的金額(越領錢越多!)", () => {
  const account = new BankAccount(20);
  const money = account.withDraw(-30);
  expect(account.balance).toBe(20);
  expect(money).toBe(0);
});
class BankAccount {
  constructor(accountBalance) {
    this.accountBalance = accountBalance;
  }
  deposit(money) {
    if (money > 0) {
      this.accountBalance += money;
    }
  }
  withDraw(money) {
    if (money > 0) {
      this.accountBalance -= money;
      return money;
    } else {
      return 0;
    }
  }
  get balance() {
    return this.accountBalance;
  }
}

test("不能領超過本身餘額", () => {
  const account = new BankAccount(20);
  const money = account.withDraw(30);
  expect(account.balance).toBe(20);
  expect(money).toBe(0);
});
class BankAccount {
  constructor(accountBalance) {
    this.accountBalance = accountBalance;
  }
  deposit(money) {
    if (money > 0) {
      this.accountBalance += money;
    }
  }

  withDraw(money) {
    if (money > 0 && this.accountBalance > money) {
      this.accountBalance -= money;
      return money;
    } else {
      return 0;
    }
  }
  get balance() {
    return this.accountBalance;
  }
}

4.現在所有測試都應該通過了也就代表完成 TDD 測試,至於 5 的重構則要看專案是否有時間以及必要進行重構,這都要看每間公司的決定。

comments powered by Disqus