《重構 改善既有程式碼的設計》 讀書筆記(十五)
第四章 構築測試體系
可靠的測試是安全重構的前提。
4.1 自測試程式碼的價值
一套測試就是一個強大的BUG偵測器,能夠大大縮短查詢BUG所需要的時間。
但我們都很懂,編寫測試程式碼,意味著額外的時間,額外的精力,除非真正地感受到這種方法對程式設計速度的提升,否則自我測試是沒有意義的。(體現不出它的意義)
在通常情況下,我們的測試是手動執行的,如果測試變得自動化,能夠自動告訴錯誤出現在哪裡,那麼測試就會變得有趣且有意義了。
撰寫測試程式碼最有用的實際是在開始編寫之前——編寫測試程式碼程式碼其實就是在問自己,這個功能需要做些什麼。編寫測試程式碼還能使你把注意力集中於介面而非實現。預先寫好的測試程式碼也為你的工作安上一個明確的結束標誌。
在Java中,測試的慣用手法是testing main,意思是每個類都應該有一個用於測試的main()。而另一種做法是,建立一個獨立類用於測試,並在一個框架中執行它,使測試工作更輕鬆。
4.2 JUnit測試框架
從現在起,我們又要開始敲程式碼了。
宣告一點:這本書用的JUnit版本比較古老,而我對JUnit其實不很熟悉,所以大致還是按著書的思路來寫,如果有必要的話,我也許會修改。
任何包含測試程式碼的類都必須繼承測試框架所提供的TestCase類。這個框架運用了設計模式之組合模式(Compoeite),允許你將測試程式碼聚集到測試套件(test suite)中。
組合模式:多用於部分-整體這樣的關係。它的目的在於把一類東西聚集起來,在外表現出同樣的行為介面。(恕我不能很好地講出,具體還是網上查+例項比較好)
在本章中,將會創造一個FileReaderTester類來測試檔案讀取器,它與測試框架的UML類圖如下:
/'線上作圖(UML)網址:
http://www.plantuml.com/plantuml/uml/SyfFKj2rKt3CoKnELR1Io4ZDoSa70000
如果要修改的的話,開啟網址後,直接複製上圖片連結(或者貼上下方程式碼)修改即可'/
@startuml
Title '測試框架的組合結構'
class FileReaderTester
namespace junit.framework #DDDDDD {
interface Test
TestCase <|- .FileReaderTester
Test <|.. TestCase
Test <|.. TestSuite
Test <- TestSuite
}
@enduml
下面開始建立FileReaderTester類。
public class FileReaderTester extends TestCase {
public FileReaderTester(String name) {
super(name);
}
}
對於老版本和新版本的JUnit而言,一個很顯著的改變是由繼承變成註解,如果之後我能提得起興趣,可能會把較新版本的JUnit也寫一遍。
這個新建的類必須有一個建構函式,完成之後就可以開始新增測試程式碼了。
首先,要設定測試夾具(test fixture),也就是樣本。在這裡供我們測試的樣本是一個檔案。
Bradman 99.94 52 80 10 6996 334 29
Pollock 60.97 23 41 4 2256 274 7
Headley 60.83 22 40 4 2256 270* 10
Sutcliffe 60.73 54 84 9 4555 194 16
在進一步運用這個檔案之前,需要準備好測試夾具。
在JUnit中的TestCase類提供兩個函式針對此用途:setUp()用來產生相關物件,tearDown()負責刪除它們。
我們需要在我們的測試類中對這兩個方法進行覆寫。(在TestCase類中並沒有為這兩個方法附帶程式碼)
public class FileReaderTester extends TestCase {
private FileReader input;
public FileReaderTester(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
try {
input = new FileReader("data.txt");
} catch (FileNotFoundException e) {
throw new RuntimeException("unable to open test file.");
}
}
@Override
protected void tearDown() throws Exception {
try {
input.close();
} catch (IOException e) {
throw new RuntimeException("error on closing test file.");
}
}
}
讓我們看看setUp()和tearDown()在jdk中的註釋吧。
/**
* Sets up the fixture, for example, open a network connection.
* This method is called before a test is executed.
* 設定裝置,例如,開啟網路連線。
* 在執行測試之前呼叫此方法。
* (簡單說,就是在測試開始前先執行的一段程式碼,可以簡單理解為構造器的作用)
*/
protected void setUp() throws Exception {
}
/**
* Tears down the fixture, for example, close a network connection.
* This method is called after a test is executed.
* 拆下裝置,例如,關閉網路連線。
* 此方法在執行測試後呼叫。
* (擦屁股的一個類,會在最後執行,類似於finally)
*/
protected void tearDown() throws Exception {
}
現在測試類準備就緒了,開始編寫測試程式碼。
首先測試read():讀取一些字元,然後檢查後續讀取的字元是否正確。這些測試方法都放在剛才我們建立的FileReaderTester類中。
public void testRead() throws IOException {
char ch = '&';
for (int i = 0; i < 4; i ++)
ch = (char) input.read();
// 斷言
assert ('d' == ch);
}
assert()扮演自動測試角色。(這是這本書之前提到的斷言,我不熟悉這個東西)
如果assert()引數值為true,一切安好;否則我們就會收到錯誤通知。
下面,我們要讓這個測試執行起來。
先在測試類中創造一個測試套件。
public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(new FileReaderTester("testRead"));
return suite;
}
在我一開始看的時候,其實是有一些懵的,不僅不知道它的作用,也不知道這麼寫為什麼沒有報錯,但是可以回過頭看看之前的那個類圖,這些繼承關係使得它不報錯。
閱讀這部分的原始碼(Test、TestSuite和TestCase),也許能更好地理解組合模式。
在這個測試套件(指代返回的TestSuite物件)中,只含有一個測試用例物件,即FileReaderTester例項。在增加測試用例時,我把待測函式的名稱以字串形式傳給建構函式,從而建立一個物件,用以測試被指定的函式。這裡是利用了Java的反射機制和物件關聯,有興趣可自行研究JUnit原始碼。
如果要讓測試跑起來,需要一個獨立的TestRunner類。
你可以寫一個GUI,也可以用控制檯。此處不用GUI。
public static void main(String[] args) {
junit.textui.TestRunner.run(suite());
}
執行起來即可。
提供suite.addTest()方法我們可以新增多個測試類,然後去統一執行測試,這就讓我們的測試更加系統化。
--------這一段我用eclipse測試時不會報錯-begin--------
public void testRead() throws IOException {
char ch = '&';
for (int i = 0; i < 4; i ++)
ch = (char) input.read();
// 斷言 本來此處的ch值應為d,此表示式false
// 在書中說這裡能顯示一個錯誤資訊,可我這裡沒有
assert ('2' == ch);
}
--------這一段我用eclipse測試時不會報錯-end--------
書中提到了另一種形式的斷言,這種我這裡可以報錯。
public void testRead() throws IOException {
char ch = '&';
for (int i = 0; i < 4; i ++)
ch = (char) input.read();
// 斷言
assertEquals('2', ch);
}
通常情況,建議在寫好測試程式碼時,先故意錯誤——這樣能保證測試機制的確執行了,並且可以報錯。
JUnit還包含一個很好的圖形使用者介面,如果所有測試順利通過,就是綠色,只要有一個測試失敗,就是紅色進度條。這是一個很方便的測試環境。
相關文章
- 《重構:改善既有程式碼的設計》讀書筆記筆記
- 《重構-改善既有程式碼的設計》讀書筆記筆記
- 《重構:改善既有程式碼的設計》讀書筆記(一)筆記
- 重構:改善既有程式碼的設計(第二版讀書筆記) - 重構、壞程式碼、寫好程式碼筆記
- 讀經典【1】重構:改善既有程式碼的設計
- 《重構——改善既有程式碼的設計》感想
- 重構改善既有的程式碼設計(重構原則)
- 重構-改善既有程式碼的設計(四)–構築測試體系
- 重構-改善既有程式碼的設計(七)– 在程式碼之間搬移特性
- 重構-改善既有程式碼的設計(六)–重新組織函式函式
- 《重構》讀書筆記筆記
- C語言程式設計讀書筆記:結構C語言程式設計筆記
- 《改善python程式的91個建議》讀書筆記Python筆記
- 《程式碼大全》讀書筆記-構建的前期筆記
- 《軟體架構設計》讀書筆記架構筆記
- 《JavaScript程式設計精解》--讀書筆記JavaScript程式設計筆記
- 反應式程式設計讀書筆記程式設計筆記
- 《Java程式設計思想》讀書筆記一Java程式設計筆記
- CSAPP 併發程式設計讀書筆記APP程式設計筆記
- 讀書筆記-Java程式設計思想-03筆記Java程式設計
- 《程式設計師的自我修養》-讀書筆記程式設計師筆記
- 《基礎設施即程式碼》讀書筆記筆記
- python高階程式設計讀書筆記(一)Python程式設計筆記
- C#高階程式設計 讀書筆記C#程式設計筆記
- 《程式設計師自我修養》讀書筆記程式設計師筆記
- 《程式設計師修煉之道》讀書筆記程式設計師筆記
- 【讀書筆記】Java併發程式設計的藝術筆記Java程式設計
- 《Go 語言程式設計》讀書筆記(十一)底層程式設計Go程式設計筆記
- 讀書筆記-乾淨程式碼筆記
- 《程式碼整潔之道——程式設計師的職業素養》讀書筆記(一)程式設計師筆記
- 《JavaScript Dom程式設計藝術》讀書筆記(一)JavaScript程式設計筆記
- 《Go 語言程式設計》讀書筆記(十)反射Go程式設計筆記反射
- 《Go 語言程式設計》 讀書筆記 (八) 包Go程式設計筆記
- 《Go 語言程式設計》讀書筆記(四)介面Go程式設計筆記
- 《Go 語言程式設計》讀書筆記 (三) 方法Go程式設計筆記
- 高效程式設計師的45個習慣 讀書筆記程式設計師筆記
- 《夢斷程式碼》讀書筆記(二)筆記
- 夢斷程式碼讀書筆記(一)筆記