TDD的實踐demo

TDD三定律

1、在編寫不能通過的單元測試前,不可編寫生產(chǎn)代碼。
2、只可編寫剛好無法通過的單元測試,不能編譯也算不通過。
3、只可編寫剛好足以通過當前失敗測試的生產(chǎn)代碼。

TDD的關鍵步驟

  • 添加一個小的測試
  • 運行所有測試并且失敗
  • 做一點修改
  • 運行所有測試并且成功
  • 重構以消除重復

基于這個思想,上codewars找一道題實踐一把tdd。

ATM machines allow 4 or 6 digit PIN codes and PIN codes cannot contain anything but exactly 4 digits or exactly 6 digits.
If the function is passed a valid PIN string, return true, else return false.
eg:
Solution.validatePin("1234") === true
Solution.validatePin("12345") === false
Solution.validatePin("a234") === false

根據(jù)題目來看,寫一個方法,輸入是一個密碼,返回一個boolean值。根據(jù)tdd關鍵步驟,先添加一個測試。
codewars已經(jīng)有示例了。

@Test
    public void validPins() {
        assertEquals(true, Solution.validatePin("1234"));
        assertEquals(true, Solution.validatePin("0000"));
        assertEquals(true, Solution.validatePin("1111"));
        assertEquals(true, Solution.validatePin("123456"));
        assertEquals(true, Solution.validatePin("098765"));
        assertEquals(true, Solution.validatePin("000000"));
        assertEquals(true, Solution.validatePin("090909"));
    }
    

接下來到第二步,運行所有測試并失敗。很顯然會失敗,因為Solution就沒有這個類,這里不運行,先創(chuàng)建solution類。(第三步做一點修改)

public class Solution {
}

good news Solution不報錯了。但是validatePin報錯了,因為還沒有這個方法。運行測試,失敗了。哦哦,來繼續(xù)做一點修改以通過測試。添加validatePin方法。

 public static boolean validatePin(String pin){
        return true;
    }

ok不報錯了。
我們看第一個測試方法validPins,全部驗證的是true。那我們直接返回true,運行測試。



測試成功了。到此結束了嗎?好像沒有,我們的測試用例沒有覆蓋到需求。還需要寫更多的測試用例以滿足需求。看看需求:四位或六位的數(shù)字。添加測試:

    @Test
    public void nonDigitCharacters() {
        assertEquals(false, Solution.validatePin("a234"));
        assertEquals(false, Solution.validatePin(".234"));
    }

    @Test
    public void invalidLengths() {
        assertEquals(false, Solution.validatePin("1"));
        assertEquals(false, Solution.validatePin("12"));
        assertEquals(false, Solution.validatePin("123"));
        assertEquals(false, Solution.validatePin("12345"));
        assertEquals(false, Solution.validatePin("1234567"));
        assertEquals(false, Solution.validatePin("-1234"));
        assertEquals(false, Solution.validatePin("1.234"));
        assertEquals(false, Solution.validatePin("00000000"));
    }

ok,這里添加了2個測試方法,一個是測試非數(shù)字的,一個是測試位數(shù)的。重復tdd步驟,運行測試。



2個失敗了,1個通過了。做一點小修改,以通過測試用例。當輸入“a234”的時候或者“.234”的時候,返回false。修改方法validatePin

public static boolean validatePin(String pin){
        if("a234".equals(pin) || ".234".equals(pin)){
            return false;
        }
        return true;
    }

這里因為練習pdd的步驟,所以每一步嚴格按照tdd的步驟走了,所以看起來這里的代碼比較蠢。
ok運行測試


image.png

棒棒的,我們嚴格滿足了定律3,只編寫剛好通過測試用例的代碼。ok,暫時搞定2個測試用例了,我們進行下一步。位數(shù)測試,修改一點點代碼滿足測試3

public static boolean validatePin(String pin){
        if("a234".equals(pin) || ".234".equals(pin)){
            return false;
        }
        if(pin.length()!=4 && pin.length()!=6){
            return false;
        }
        return true;
    }

運行測試



全通過了,進行下一步,重構以消除重復。
現(xiàn)在的方法看起來是這樣

public static boolean validatePin(String pin){
        if("a234".equals(pin) || ".234".equals(pin)){
            return false;
        }
        if(pin.length()!=4 && pin.length()!=6){
            return false;
        }
        return true;
    }

看到問題了,里面有硬編碼,所以我們要消除硬編碼。這段硬編碼我們是為了滿足測試2,測試2是為了測試是不是純數(shù)字,做一點小修改

public static boolean validatePin(String pin){
        Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
        Matcher isNum = pattern.matcher(pin);
        if (!isNum.matches()) {
            return false;
        }
        if(pin.length()!=4 && pin.length()!=6){
            return false;
        }
        return true;
    }

運行測試,ok還是通過了。
下一步,重構以消除重復。這個方法細分來看,做了2件事,第一是判斷是否包含數(shù)字之外的字符,第二是判斷位數(shù),那么把這兩個事可以單獨提出來提煉一個方法。重構如下

public class Solution {
    public static boolean validatePin(String pin){
        return validateNonDigitCharacters(pin) && invalidLengths(pin);
    }
    private static boolean validateNonDigitCharacters(String pin){
        Pattern pattern = Pattern.compile("^-?\\d+(\\.\\d+)?$");
        Matcher isNum = pattern.matcher(pin);
        return isNum.matches();
    }
    private static boolean invalidLengths(String pin){
        return !(pin.length()!=4 && pin.length()!=6);
    }
}

invalidLengths方法似乎還有些不是很能一眼看懂,不過仔細看看還是能懂,guess what,我不改了。
運行測試



測試通過了,到此結束。


總結

我把tdd理解為 邏輯代碼未動,測試代碼先行。通過不停的滿足覆蓋全面的測試代碼,一點點修改邏輯代碼,直到滿足所有測試。

TODO

可以在原需求基礎增加一個需求,繼續(xù)迭代開發(fā),體現(xiàn)出tdd的優(yōu)勢

bug

測試用例沒有覆蓋完全,沒有考慮空值

最優(yōu)解

public static boolean validatePin(String pin) {
    return pin.matches("\\d{4}|\\d{6}");
  }
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容