網絡代碼被證明是很難進行完全徹底的測試
這是因為測試組件不依賴其他服務器
以獨立進程形式工作時效果最好
本文中
Nelson Minar描述了兩種單元測試網絡代碼的方法
首先
他提出您設計網絡代碼時應該盡可能地做到邏輯上與網絡獨立
接著
他建議使用Java的協議處理器類模擬網絡連接而不是使用實際的網絡
使用這些原則
您就可以很輕松地生成網絡測試軟件
測試網絡代碼並是一件很困難的事情
優秀的單元測試組件運行速度非常快
這樣開發人員在每次編譯之後就能夠進行測試
當然
測試流也要能夠穩定地運行
這樣它們才可以持續捕獲代碼中的任何錯誤
然而
實踐證明
網絡代碼(例如
從URL上讀取的代碼)是很難快速並穩定地測試的
而且
如果測試組件本身進行網絡調用
測試會因為依賴網絡和其他服務器將會變得非常緩慢並十分不穩定
設想一個可以從網頁上下載
格式化並顯示XML數據的程序
該程序的本地測試流將需要從一個運行的Web服務器上獲取XML數據
但是程序的很多部分——XML解析器
格式化程序和顯示程序——可能不需要依賴網絡就可以獨立測試
請記住這個例子
我將在本文中舉例說明兩種可以測試與網絡相關代碼的方法
當測試進行時
這兩種方法可以避免使用網絡
我首先描述簡單的網絡激活演示程序PrintRSS
然後再討論如何使用簡單的 Reader 和Writer 對象而不是網絡連接來設計簡化測試的PrintRSS程序
最後我將介紹一個允許程序員合成特殊的testurl庫
使用正常http中的URLs繞過網絡
注意
測試將使用JUnit 測試框架的 assert() 方法
PrintRSS演示程序
PrintRSS是一個可以從URL讀取數據並對數據進行處理的程序
它可以很好的演示網絡代碼的測試
PrintRSS 使用RSS格式讀取數據
這個數據格式可以簡單地將新鮮內容並入XML
本文中
這個重要的RSS結構定義如下
Channel Title
- Item
- Item
PrintRSS從某個URL下載RSS文檔
規定內容的具體布局
然後以一種易讀的方式將標題輸出到System
out
Channel Title
Item
Item
PrintRSS執行四個主要操作
● 打開與某一URL的連接
● 使用XML進行讀取
● 格式化數據
● 輸出到System
out
PrintRSS程序將上述的四種功能封裝在一個單獨的方法(printURL(URL))中
然而
很難對這個方法進行測試
原因有兩個
首先代碼依賴於從URL上讀取的數據
如果URL 是一 URL
這就要涉及到網絡
而且
輸出到System
out所產生的影響使代碼自己的行為也被隱藏起來
好好考慮一下這些問題
您又能如何更好地設計Printress來進行測試呢?
使用eaders和writers封裝數據
簡單地解析和格式化XML代碼而不是連接網絡
您就能分解代碼並獨立測試數據的邏輯性
雖然再分解代碼看起來有點令人畏懼
但是這樣的努力是為了得到更好的代碼
這是因為代碼是經過測試的同時設計也更標准
記住
您可以將printURL()的代碼解析和格式化功能分解為一個新的方法formatReader(Reader
Writer)
這個方法專門用於對一個帶有XML數據的Reader對象進行解析然後將結果報告輸出到提供的Writer
測試 formatReader(Reader
Writer)現在變得簡單了
testFormatReaderGoodData():
String goodRSSData =
+
Channel Title +
- Item
+
- Item
+
;
String goodRSSOutput =
Channel Title\n Item
\n Item
\n
;
Reader r = new StringReader(goodRSSData);
Writer w = new StringWriter();
PrintRSS
formatReader(r
w);
assertEquals(goodRSSOutput
w
toString());
上面的示例只用readers和writers在沒有URL和網絡連接的情況下測試了解析和格式化邏輯
測試示例演示了一個有用的測試方法
創建的reader流將測試數據包含在測試代碼中而不是從文件或者網絡讀取數據
實踐證明StringReader和StringWriter(或者 ByteArrayInputStream 和ByteArrayOutputStream)在把測試數據嵌入到單元測試流方面是沒有價值的
上述的單元測試在一切都正常時執行一定的邏輯進行觀察
但它對問題出現錯誤處理代碼同樣重要
接下來
就是一個測試壞數據的示例
其中巧妙的使用了Junit來檢查是否出現異常
testFormatReaderBadData():
String badXMLData =
this is not valid xml data
;
StringReader r = new StringReader(badXMLData);
try {
PrintRSS
formatReader(r
new StringWriter());
fail(
should have thrown XML error
);
} catch (XMLParseException ex) {
// No error
we expected an exception
}
readers 和 writers再次封裝數據
主要的不同在於
這裡的數據使 formatReader()拋出異常
如果沒有產生異常
就會調用Junit的方法fail()
使用Java 協議處理器測試網絡代碼
雖然非網絡的分離方法可以使程序能像PrintRSS一樣易於測試
但是這樣的努力對於創建完整的測試流還是不夠的
我們仍希望測試網絡代碼本身
尤其是網絡異常控制
而且有時候分離到reader接口並不是很方便
被測試的代碼可能會依賴於一個僅能理解URL的庫
本部分我將解釋如何測試方法 formatURL(URL
Writer)
這種方法帶有URL
讀取RSS並將數據輸出到writer
您可以使用多種方式測試包含URL的代碼
首先
您可以使用標准 URLs 並指向運轉的測試服務器
然而這種方法要求穩定的Web服務器——少一個組件來使測試復雜化
其次
您可以使用指向本地文件的 file: URLs
這種方法和使 URLs存在相同的問題
雖然您可以使用file:或者 URLs訪問准確的測試數據
但是它仍然很難模擬導致I/O(輸入和輸出)異常的網絡失敗
一個測試URL代碼的更好方法就是創建新的URL命名空間testurl:
使之處於測試程序的完全控制之中——種使用Java協議處理器的簡單方法
實現sturl
Java協議處理器允許編程人員使用它們自己的代碼實現協議
協議處理器很簡單
首先
滿足Java編寫協議處理器的需求
要處理好三個重要的部分
● 類TTestURLConnection
URLConnection的實現可以處理返回輸入流
標題等實際方法
● 類 Handler
將testurl: URL轉換成 TestURLConnection
● 屬性java
protocol
handler
pkgs
表示何處找到新的URL命名空間實現的系統屬性
其次
提供一個有用的TestURLConnection執行過程
在這種情況下
庫用戶看不到實際的 TestURLConnection
相反
工作是通過類TestURLRegistry進行的
這個類只有一個單獨的方法
TestURLRegistry
register(String
InputStream)
它將輸入流和給訂的URL聯系在一起
例如
InputStream inputIS =
;
TestURLRegistry
register(
data
inputIS);
如此安排使得開放的URL testurl:data返回inputIS中的輸入流
實現了這一點
您就能很容易的構建測試程序控制輸出的URL
(注意
輸入流數據並沒有復制
因此
通常情況下每一個URL您只能使用一次
)
對准確數據的測試
首先我們使用testurl
用准確數據測試formatURL()
testFormatURLGoodStream():
InputStream dataIS = new ByteArrayInputStream(goodRSSData
getBytes());
TestURLRegistry
register(
goodData
dataIS);
URL testURL = new URL(
testurl:goodData
);
Writer w = new StringWriter();
PrintRSS
formatURL(testURL
w);
assertEquals(goodRSSOutput
w
toString());
上面的代碼和先前的測試使用相同的數據
但這裡卻把數據與testurl:goodData URL聯系起來
方法formatURL() 打開並讀取URL
測試代碼保證數據能夠正確讀取
格式化並被輸出到Writer
模擬失敗
用網絡環境編寫程序很容易
但編寫可以正確處理網絡失敗的代碼卻比較困難
testurl
能夠模擬網絡失敗——這是該方法的主要優點
首先
模擬連接到某一URL的網絡失敗是不可能的
這種情形的一個典型代表就是遠程web服務器沒有工作
我們的testurl:庫包含一個它能理解的URL
testlurl:errorOnConnect 將保證只要您連接到這個URL
它就會拋出一個IOException
您可以使用這個URL
並對程序是否能正確處理URL不能訪問的情況進行測試
testFormatURLNoConnection():
URL noConnectURL = new URL(
testurl:errorOnConnect
);
Writer w = new StringWriter();
try {
PrintRSS
formatURL(noConnectURL
w);
fail(
Should have thrown IO exception on connect
);
} catch (IOException ioex) {
}
如果連接失敗
上面的測試保證formatURL()可以正確地拋出一個IOException
但是
您將如何測試這樣一種情形(起初網絡連接正常工作但在交互期間失敗)呢?
因為您已經完全控制了與測試URL相關的輸入流
您就可以選用任意一種輸入流實現
例如
您創建一個簡單的類BrokenInputStream
它用方法read()封裝輸入流
而方法read()在拋出異常IOException之前只允許讀取一定字節數目的數據
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26243.html