說了那么多,讓我們用示例看看,系統重構是應該怎樣做自動化測試的。還是回到前面那個HelloWorld的例子(詳見 3.3 小步快跑是這樣玩的),該類中有一個sayHello()方法,只要我們輸入當前的時間與用戶名,就返回對該用戶的問候語。如果當前時間是上午,則返回“Hi, XXX. Good morning!”;如果是下午,則返回“Hi, XXX. Good afternoon!”;如果是晚上,則返回“Hi, XXX. Good Night!”,這是HelloWorld這個程序實現的功能。
然后我們開始為這段程序編寫測試代碼(如果采用測試驅動開發,應當先寫測試代碼再寫程序)。我們首先建立一個test源程序目錄,然后建立與被測程序對應的包和測試程序。這就是說,如果被測程序在“org.refactoring.helloWorld.resource”包中,則測試程序應當建立“test.org.refactoring.helloWorld.resource”包與之對應;如果被測程序叫“HelloWorld”,則建立“HelloWorldTest”類與之對應,這個類是一個JUnit測試程序。
下面就是編寫這個測試程序執行測試了。由于被測程序有三個分支,即當前時間是上午、下午、晚上,因此我們分別為之建立了三個測試用例,測試程序如下:
這段程序采用的是JUnit4編寫的,其中assertThat(result, is("Hi, IT攻城獅. Good night!"));,第一個參數是被測程序執行的結果,而第二個參數是根據期望結果進行驗證。如果執行結果與預期結果相同,則測試通過,否則測試失敗。
隨后我們運行該測試程序,得到如下結果:
三項測試用例全部通過,測試成功!
現在我們為原程序編寫了測試用例并全部測試通過,我們為重構所做的準備工作就一切就緒了。然后,我們開始進行第一次重構。如前面所述,第一次重構我們調整了程序的順序,進行了分段,增加了注釋,并修改了相應的變量,使其更加利于閱讀。這是一個小步快跑的過程,我們完成此次重構只花費了3、5分鐘。當重構完成,程序重新回到可編譯運行狀態時,我們執行它的這個測試程序,測試通過。測試通過意味著,雖然程序內部的代碼有所修改,但程序對外的功能沒有變化,即程序的外部行為沒有變化,則重構成功,我們可以繼續后面的工作。
第二次重構,我們運用“抽取方法”,從sayHello()函數中抽取出了getFirstGreeting(), getSecondGreeting(), getHour()三個方法。之后我們再次執行測試程序,測試通過。
第三次重構,我們運用“抽取類”,將getFirstGreeting()與getSecondGreeting()分別抽取出來形成了GreetingToUser和GreetingAboutTime。完成之后執行測試通過。
第四次重構,我們的需求發生了變化,問候語不僅隨一天中的上午、下午、晚上等進行變化,還需要根據不同的日期判斷是否是節日。在這種情況下,我們采用“兩頂帽子”的方式進行開發:首先不引入新的需求,僅僅修改原程序,使之適應新需求。為此我們從GreetingAboutTime類中提煉出DateUtil,使之不僅有getHour(),還有getMonth()與getDate()。完成重構以后測試通過。
關于“兩頂帽子”的設計方式,也是系統重構中另一個不同以往的地方,我們還將在后面詳細地進行討論。隨后我們開始添加新需求,使GreetingAboutTime中的getGreeting()寫成這樣:
之后我們的測試不能通過:
為什么testSayHelloAtNight測試不能通過呢?仔細查看被測程序,我們發現它的功能發生了變化,變為:如果當前時間是1月1日,則返回“Hi, XXX. Happy new year!”;如果是1月14日,則返回“Hi, XXX. Happy valentine's day!”……如果當前時間都不是這些節日,如果是上午則返回“Hi, XXX. Good morning!”,是中午則返回“Hi, XXX. Good noon!”,是下午則返回“Hi, XXX. Good afternoon!”,是傍晚則返回“Hi, XXX. Good evening!”,否則才返回“Hi, XXX. Good night!”。正因為如此,我們需要調整我們的測試程序,為每一個分支編寫測試用例。測試修改好后,最后測試通過。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
然后我們開始為這段程序編寫測試代碼(如果采用測試驅動開發,應當先寫測試代碼再寫程序)。我們首先建立一個test源程序目錄,然后建立與被測程序對應的包和測試程序。這就是說,如果被測程序在“org.refactoring.helloWorld.resource”包中,則測試程序應當建立“test.org.refactoring.helloWorld.resource”包與之對應;如果被測程序叫“HelloWorld”,則建立“HelloWorldTest”類與之對應,這個類是一個JUnit測試程序。
下面就是編寫這個測試程序執行測試了。由于被測程序有三個分支,即當前時間是上午、下午、晚上,因此我們分別為之建立了三個測試用例,測試程序如下:
/** * Test for {@link org.refactoring.helloWorld.resource.HelloWorld} * @author fangang */ public class HelloWorldTest { private HelloWorld helloWorld = null; /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { helloWorld = new HelloWorld(); } /** * @throws java.lang.Exception */ @After public void tearDown() throws Exception { helloWorld = null; } /** * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}. */ @Test public void testSayHelloInTheMorning() { Date now = DateUtil.createDate(2013, 9, 7, 9, 23, 11); String user = "鮑曉妹"; String result = ""; result = helloWorld.sayHello(now, user); assertThat(result, is("Hi, 鮑曉妹. Good morning!")); } /** * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}. */ @Test public void testSayHelloInTheAfternoon() { Date now = DateUtil.createDate(2013, 9, 7, 15, 7, 10); String user = "關二鍋"; String result = ""; result = helloWorld.sayHello(now, user); assertThat(result, is("Hi, 關二鍋. Good afternoon!")); } /** * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}. */ @Test public void testSayHelloAtNight() { Date now = DateUtil.createDate(2013, 9, 7, 21, 30, 10); String user = "IT攻城獅"; String result = ""; result = helloWorld.sayHello(now, user); assertThat(result, is("Hi, IT攻城獅. Good night!")); } }
這段程序采用的是JUnit4編寫的,其中assertThat(result, is("Hi, IT攻城獅. Good night!"));,第一個參數是被測程序執行的結果,而第二個參數是根據期望結果進行驗證。如果執行結果與預期結果相同,則測試通過,否則測試失敗。
隨后我們運行該測試程序,得到如下結果:

圖4.1 JUnit測試結果
三項測試用例全部通過,測試成功!
現在我們為原程序編寫了測試用例并全部測試通過,我們為重構所做的準備工作就一切就緒了。然后,我們開始進行第一次重構。如前面所述,第一次重構我們調整了程序的順序,進行了分段,增加了注釋,并修改了相應的變量,使其更加利于閱讀。這是一個小步快跑的過程,我們完成此次重構只花費了3、5分鐘。當重構完成,程序重新回到可編譯運行狀態時,我們執行它的這個測試程序,測試通過。測試通過意味著,雖然程序內部的代碼有所修改,但程序對外的功能沒有變化,即程序的外部行為沒有變化,則重構成功,我們可以繼續后面的工作。
第二次重構,我們運用“抽取方法”,從sayHello()函數中抽取出了getFirstGreeting(), getSecondGreeting(), getHour()三個方法。之后我們再次執行測試程序,測試通過。
第三次重構,我們運用“抽取類”,將getFirstGreeting()與getSecondGreeting()分別抽取出來形成了GreetingToUser和GreetingAboutTime。完成之后執行測試通過。
第四次重構,我們的需求發生了變化,問候語不僅隨一天中的上午、下午、晚上等進行變化,還需要根據不同的日期判斷是否是節日。在這種情況下,我們采用“兩頂帽子”的方式進行開發:首先不引入新的需求,僅僅修改原程序,使之適應新需求。為此我們從GreetingAboutTime類中提煉出DateUtil,使之不僅有getHour(),還有getMonth()與getDate()。完成重構以后測試通過。
關于“兩頂帽子”的設計方式,也是系統重構中另一個不同以往的地方,我們還將在后面詳細地進行討論。隨后我們開始添加新需求,使GreetingAboutTime中的getGreeting()寫成這樣:
/** * @return the greeting about time */ public String getGreeting(){ DateUtil dateUtil = new DateUtil(date); int month = dateUtil.getMonth(); int day = dateUtil.getDay(); int hour = dateUtil.getHour(); if(month==1 && day==1) return "Happy new year! "; if(month==1 && day==14) return "Happy valentine's day! "; if(month==3 && day==8) return "Happy women's day! "; if(month==5 && day==1) return "Happy Labor day! "; ...... if(hour>=6 && hour<12) return "Good morning!"; if(hour==12) return "Good noon! "; if(hour>=12 && hour<19) return "Good afternoon! "; if(hour>=19 && hour<22) return "Good evening! "; return "Good night! "; }
之后我們的測試不能通過:

圖4.2 測試用例不能通過
為什么testSayHelloAtNight測試不能通過呢?仔細查看被測程序,我們發現它的功能發生了變化,變為:如果當前時間是1月1日,則返回“Hi, XXX. Happy new year!”;如果是1月14日,則返回“Hi, XXX. Happy valentine's day!”……如果當前時間都不是這些節日,如果是上午則返回“Hi, XXX. Good morning!”,是中午則返回“Hi, XXX. Good noon!”,是下午則返回“Hi, XXX. Good afternoon!”,是傍晚則返回“Hi, XXX. Good evening!”,否則才返回“Hi, XXX. Good night!”。正因為如此,我們需要調整我們的測試程序,為每一個分支編寫測試用例。測試修改好后,最后測試通過。
大話重構連載首頁: http://fangang.iteye.com/blog/2081995
特別說明:希望網友們在轉載本文時,應當注明作者或出處,以示對作者的尊重,謝謝!
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
