# [12/12]TDD 與重構工作坊 #### [https://hackmd.io/@7900XTX/TDD](https://hackmd.io/@7900XTX/TDD) ## TDD (Test-Driven Development) 開發流程 TDD在開發過程中不斷利用單元測試 (Unit Test),確保程式的功能正確。 有別於直接開始實作功能的開發流程,TDD注重於先寫出測試,再寫出能通過測試的程式。 ![image](https://hackmd.io/_uploads/HJWQQbiVkg.png =75%x) ### <font color="red">1. 寫測試</font> 在開始實作程式邏輯之前,先寫測試。一旦最終完成的程式能夠通過測試案例,則代表程式正確,因為程式的正確性驗證已經透過測試確認完畢。 * #### 列測項 先從簡單的案例開始,接著列出具有代表性、邏輯分支的測試案例。 * #### 定介面 設計即將實作程式之使用介面、函式名稱、參數等。 在開始實作程式功能之前,先進行一次測試,確保<font color=#ff5e00>*不會*</font>通過測試。 :::info * <font color=#999999>訂出介面`convert`、`getAns`,與包含1個參數的建構子`FizzBizz`。 * 當輸入為1時,輸出應為1。</font> ```java= @Test void test_1() { FizzBizz sut = new FizzBizz(1); sut.convert(); String actual = sut.getAns(); assertEquals("1",actual); } ``` * <font color=#999999>由於還沒開始寫程式功能,因此不應通過測試。</font> ![image](https://hackmd.io/_uploads/r1XvbXsEJe.png) ::: ### <font color=#00c22d>2. 寫程式使通過測試</font> > Work -> Correct -> Fast 可先直接回傳正確答案,確保程式與測試能正常運行。 成功通過測試後,用良好風格的程式實作真正的功能與邏輯。 :::info * <font color=#999999>直接回傳正確答案,確保測試功能正常。</font> ```java= public String getAns() { return "1"; } ``` ![image](https://hackmd.io/_uploads/SkqjXQiVkg.png) * <font color=#999999>逐步增加具代表性的測項</font> ```java= class FizzBizzTest { @Test void test_1() { FizzBizz sut = new FizzBizz(1); sut.convert(); String actual = sut.getAns(); assertEquals("1",actual); } @Test void test_2() { FizzBizz sut = new FizzBizz(2); sut.convert(); String actual = sut.getAns(); assertEquals("1 2",actual); } @Test void test_3() { FizzBizz sut = new FizzBizz(3); sut.convert(); String actual = sut.getAns(); assertEquals("1 2 Fizz",actual); } @Test void test_15() { FizzBizz sut = new FizzBizz(15); sut.convert(); String actual = sut.getAns(); assertEquals("1 2 Fizz 4 Bizz Fizz 7 8 Fizz Bizz" + " 11 Fizz 13 14 FizzBizz", actual); } } ``` * <font color=#999999>修改程式功能,使其能通過更多進階的測試案例。</font> ```java= public class FizzBizz{ private int number; private String answer = ""; public FizzBizz(int number) { this.number = number; } private void process(int i) { if(!answer.isEmpty()) answer = answer + " "; if(i%3==0) answer = answer + "Fizz"; if(i%5==0) answer = answer + "Bizz"; if(i%3!=0 && i%5!=0) answer = answer + String.valueOf(i); } public void convert() { for (int i = 1; i <= this.number; i++) process(i); } public String getAns() { return answer; } } ``` ::: ### <font color= #4dbeff> 3. 進行重構</font> 在通過測試 (綠燈)後,找出可以重構的地方進行重構,測試也可能有重構的空間或必要。 重構完畢後,須確保能通過測試。 TDD在開發過程中,不斷進行測試與重構。 在專案還處於較小規模就進行重構,避免堆積成大專案時難以重構。 * #### 不可同時修改程式與測試 每次只能更改一部分程式,待通過測試綠燈後才能再改其他部分。 為避免程式與測試之邏輯皆錯誤,但錯誤的邏輯互相抵銷後,反倒通過測試,因此不可同時修改程式與測試。 在通過測試後若需更動`程式`,應確保`測試`不變,待新程式通過測試後,才能更動`測試`,反之亦然。 :::info * <font color=#999999>將程式進行重構,預想未來若有其他數字的需求 (`Line 21`),具有較好的維護與擴充性。 </font> ```java= public class FizzBizz{ private int number; private String answer = ""; public FizzBizz(int number) { this.number = number; } private String apply(int i, String currAns, int div, String word) { if (i % div == 0) { currAns += word; } return currAns; } public void convert() { List<String> result = new ArrayList<>(); for(int i=1;i<=number;i++) { String answer = ""; answer = apply(i,answer,3,"Fizz"); answer = apply(i,answer,5,"Bizz"); // answer = apply(i,answer,7,"7izz"); if(answer.isEmpty()) { answer = "" + i; } result.add(answer); } this.answer = String.join(" ", result); } public String getAns() { return answer; } } ``` ::: ## 適合TDD的場景 * 新專案需求明確,可列出測項 * 拼接舊專案的部分 * 修補bug --- ## 課後心得 本次業界工作坊講題為測試驅動開發 (Test-Driven Development, TDD),並在課堂上以Fizz Buzz遊戲作為範例,學習TDD的開發流程,而我覺得在課堂上的即時練習非常有用,能在實作的過程中更加深刻地體會TDD的過程, 透過課堂上的互動,我也更深刻體驗到開放封閉原則的重要性。在軟體開發的過程中,可能會面臨到不斷有不合理的新要求提出,開發結束後仍可能會面臨到功能新增或修改,因此程式具有良好的維護與擴充性是非常重要。 在這堂課中,我學習到新的一種軟體工程專案開發的視角,在測試、主要程式與重構的不斷循環下,使專案在開發的過程,能在功能正確的前提下,不斷前進並維持良好架構。