[解鎖新姿式] 兄dei 我感受你在寫bug

前言:

繼上篇 [解鎖新姿式] 兄dei,你代碼須要優化了 介紹一些代碼的優化的小技巧java

可是咱們除了在代碼編寫上須要優雅, 還須要編寫對應的測試用例, 以此來保證代碼的質量。git

在這篇咱們繼續在學習如何編寫有保證質量的代碼。github

背景

在剛剛學習編程的時候,因爲沒有接觸過單元測試/TDD 相關知識, 只是知道有這麼回事,不覺得然。致使工做的時候,拿到一個新需求,只知道埋頭苦寫。會出現如下場景:編程

產品:增長一個新功能 blalala.
我:好的沒問題,一個星期擼出來
一個月後.......
我:個人接口寫好了!能夠測試了。
測試:你本身測過沒 ??
我:放心 postman 測過了,穩的一批。(/滑稽)
一分鐘後.......
測試:你的接口又雙叕 報 500 了 (黑人問號??) 我:不可能!確定是你的問題api

一次偶然的機會,接觸到 TDD 開發模式,今後打開新的世界。原來編程還能夠這麼玩 : )框架

TDD

TDD是測試驅動開發(Test-Driven Development)的英文簡稱, 是敏捷開發中的一項核心實踐和技術,也是一種設計方法論。TDD的原理是在開發功能代碼以前,先編寫單元測試用例代碼,測試代碼肯定須要編寫什麼產品代碼。post

說白了,就是開發功能代碼以前,先編寫測試用例代碼`單元測試

咱們先來看一個「經典」的 TDD 問題,入手學習 TDD 開發,感覺 TDD 開發的魅力所在學習

需求:

編寫一個程序,打印出從1到100的數字,將其中3的倍數替換成「Fizz」,5的倍數替換成「Buzz」。既能被3整除、又能被5整除的數則替換成「FizzBuzz」。以下圖所示 測試

分析

在動手寫代碼以前,咱們要首先弄清需求的範圍優先級,例如給出的FizzBuzz 需求,1-100就是範圍,對應優先級是1,肯定了需求的範圍和優先級後,而後咱們就能夠開始coding~

編碼

Step1

咱們首先新建一個FizzBuzz測試類

public class FizzBuzzTest {

    @Test
    void show_raw_number(){
        FizzBuzz fizzbuzz = new FizzBuzz(1);
        assertThat(fizzbuzz.getResult()).isEqualTo("1");
    }
}

public class FizzBuzz {

    public FizzBuzz(int input) {
    }

    public String getResult() {
        return "";
    }
}
複製代碼

舒適提示:

  • 測試類推薦使用 駝峯式命名,測試方法 推薦使用下劃線命名
  • 這裏我使用的是 AssertJ 單元測試框架,這款框架主要亮點之一是支持鏈式調用。
  • assertThat方法是org.assertj.core.api.Assertions.assertThat靜態方法.
  • assertThat(A).isEqualTo(B),意思是 斷言(預測) A 等於 B,若是 A == B的話,返回 True 表示經過測試用例,不然 False,表示測試失敗,不經過。
  • 更多的使用方法能夠參考AssertJ 官方網站

咱們先建立一個 FizzBuzz 類, 建立 getResult()方法,空實現, 傳入參數 1,斷言(預期)fizzbuzz 輸出結果是 1,先運行測試用例,會出現以下效果

小夥伴:你會不會寫啊,這麼簡單的還用測試?還報錯!!
彆着急,測試驅動開發,講究是的按部就班的節奏
聽個人,往下看 相信你會 getfeel :)

看到紅燈 程序提示,咱們預期結果(Expected)是 「1」, 但實際結果(Actual)是0,說明咱們的程序有錯。
接下來咱們進一步來修改咱們的測試用例,以此來經過測試用例。 修改以下:

public String getResult() {
    return "1";
}
複製代碼

而後再次運行咱們的測試用例

舒適提示:
編寫剛恰好經過測試用例的代碼。

此次「完美」經過測試!而後咱們開始編寫下一個測試用例~

完美?? 黑人問號?? 拿刀哪一個,先把刀放下 你聽我說。

Step2

咱們來測試 第一種狀況,將其中3的倍數替換成「Fizz」,編寫 3 的測試用例

@Test
public void show_fizz(){
    FizzBuzz fizzBuzz = new FizzBuzz(3);
    assertThat(fizzBuzz.getResult()).isEqualTo("Fizz");
}
複製代碼

建立一個show_fizz 方法, 此次輸入參數爲 3,而後繼續運行測試用例

看到咱們熟悉的 「紅燈報錯」,而後我能夠繼續修改咱們的 getResult 方法 以此來經過測試用例。

public String getResult() {
    if (input % 3 == 0){
        return "Fizz";
    }
    return String.valueOf(input);
}
複製代碼

而後再次運行測試用例~

綠色!!經過測試!

看到這裏,咱們能夠發現一個規律

紅燈行,綠燈停
當測試用例是「紅燈」 時,咱們就應該動手編寫出,能經過測試的測試用例。
當測試用例是 「綠燈」時,咱們就應該停下來思考,下一個測試用例改如何編寫。

Step3

咱們繼續小步前進,繼續編寫第二種狀況,5的倍數替換成「Buzz」, 編寫 入參 5 的測試用例

@Test
public void show_buzz(){
    FizzBuzz fizzBuzz = new FizzBuzz(5);
    assertThat(fizzBuzz.getResult()).isEqualTo("Buzz");
}
複製代碼

不出意外的話,是 紅燈

咱們繼續修改getResult()方法,以此來經過測試用例。

public String getResult() {
    if (input % 3 == 0){
        return "Fizz";
    }

    // 新增
    if (input % 5 == 0){
        return "Buzz";
    }

    return String.valueOf(input);
}
複製代碼

而後繼續運行測試用例,綠燈經過~

Step4

咱們繼續小步前進,考慮第三種狀況,既能被3整除、又能被5整除的數則替換成「FizzBuzz」,編寫 入參 15 的測試用例

@Test
public void show_fizz_buzz(){
    FizzBuzz fizzBuzz = new FizzBuzz(15);
    assertThat(fizzBuzz.getResult()).isEqualTo("FizzBuzz");
}
複製代碼

這裏相信你們能猜到結果,紅燈,這裏我就演示結果了。而後咱們修改對應 getResult 方法,經過測試用例

public String getResult() {
    if (input % 15 == 0){
        return "FizzBuzz";
    }

    if (input % 3 == 0){
        return "Fizz";
    }

    if (input % 5 == 0){
        return "Buzz";
    }

    return String.valueOf(input);
}
複製代碼

重構

Step1

咱們編寫完測試用例,可是代碼出現了代碼的壞味道——重複代碼,有了測試用例,咱們就能夠很輕鬆的重構代碼,接下來開始重構咱們的代碼。

public String getResult() {
    if (isDivisibleBy(15)){
        return "FizzBuzz";
    }

    if (isDivisibleBy(3)){
        return "Fizz";
    }

    if (isDivisibleBy(5)){
        return "Buzz";
    }

    return String.valueOf(input);
}

private boolean isDivisibleBy(int i) {
    return input % i == 0;
}
複製代碼

抽取一個 isDivisibleBy 方法,而後運行測試,看看修改後是否引入bug

測試經過,沒問題,但在這裏咱們須要注意一點

每一次修改,都需運行一遍測試,避免修改引入bug,確保代碼的正確性

Step2

測試經過後,咱們繼續進一步優化。

public String getResult() {
    String result = "";
    if (isDivisibleBy(3)) {
        result += "Fizz";
    }

    if (isDivisibleBy(5)) {
        result += "Buzz";
    }

    return result;
}
複製代碼

繼續修改 getResult 方法,提取一個變量 result 做爲返回值,而後運行測試。

出乎意料,此次修改居然出現bug,正如剛剛所說,每次修改後,都須要運行測試用例保證代碼的正確性。咱們再來檢查一下代碼,並做出以下修改:

public String getResult() {
    String result = "";
    if (isDivisibleBy(3)) {
        result += "Fizz";
    } 
        
    if (isDivisibleBy(5)) {
        result += "Buzz";
    } 
        
    if (result.isEmpty()){
        result += input + "";
    }

    return result;
}
複製代碼

經過測試用例,發現當輸入爲1的時候,沒有進行處理,添加相應判斷。再次運行測試用例

測試經過,大功告成!

最後

最後剩下遍歷 1~100 狀況,相信難不倒你,感興趣的朋友,能夠自行編寫~

至此,我儘可能演示一個相對完整的TDD開發流程,麻雀雖小,五臟俱全。但願你能感覺到 getfeel ~

咱們再來總結一下:

  • 一、建立測試類
    • 名推薦使用 駝峯式命名,測試方法 推薦使用下劃線命名
  • 二、分析需求範圍優先級
  • 三、根據優先級編寫寫測試用例
  • 四、再編寫業務代碼
  • 五、而後編寫測試用例恰好經過的代碼
    • 紅燈行,綠燈停
      • 當測試用例是「紅燈」 時,咱們就應該動手編寫出,能經過測試的測試用例。
      • 當測試用例是 「綠燈」時,咱們就應該停下來思考,下一個測試用例改如何編寫。
  • 五、根據測試用例,重構,優化代碼。
    • 每一次修改,都需運行一遍測試,避免修改引入bug,確保代碼的正確性
  • 六、最終完成功能

第5點,不分前後,能夠交替執行。

最後的最後

有些人以爲爲何要弄那麼複雜,還要浪費時間寫單元測試,爲什麼不一把梭呢?
沒有測試用例覆蓋的代碼,你敢保證代碼的質量嗎?
沒有測試用例覆蓋的代碼,你敢進行代碼重構嗎? 你品你細品 (滑稽)

其實TDD 開發模式,主要看開發者意願,不強求,可是單元測試 必須的!

以上就是所有內容,但願能幫助到你~ 若有不妥,歡迎指出,你們一塊兒交流學習。

相關文章
相關標籤/搜索