您應該創建自己的界面來包裝與java.io.Console
的所有交互,例如,
public interface ConsoleService {
...
}
只要你只通過的ConsoleService
實例的控制檯交互,那麼你就可以嘲笑你的代碼的ConsoleService
和測試99%,你通常會。接口將成爲應用程序的邊界,用於整個應用程序的功能測試以及直接與其交互的類的單元測試。
現在我們已經將問題的範圍縮小爲「如何測試ConsoleService
實施」,我們需要獲得一點創意。例如,您可以將控制檯輸出重定向到一個文件並檢查該文件的內容。您可能甚至不想測試Scala中的ConsoleService
;您可以使用ConsoleService
編寫一個框架應用程序,然後使用您選擇的腳本語言在您最喜歡的操作系統上啓動一個真正的控制檯,與您的骨架應用程序進行交互並以此方式測試ConsoleService
。你可以得到像你這樣的創意(和哈克),因爲:
- 它隻影響少量的測試;和
- 你的應用程序很可能會成熟到一個點,其中
ConsoleService
實現不需要改變很多,即你的古怪測試解決方案將不會成爲未來開發人員的重大負擔。
由於這些原因,應該是顯而易見的,這是一個好主意,讓ConsoleService
包裝非常薄,因爲沒有任何邏輯將通過奇怪ConsoleService
測試進行測試,而不是漂亮的友好斯卡拉測試。通常直接授權到java.io.Console
方法已經足夠好了,但您應該允許應用程序的功能測試來驅動接口而不是做出任何推定(您的功能測試斷言可能依賴於與模擬ConsoleService
的特定交互,或者可能取決於狀態的一個存根,您可以在測試中控制的ConsoleService
的測試實現)。
最後,你可能會認爲ConsoleService
包裝是,所以很薄,它的實現確實需要任何單元/功能測試。 ConsoleService
的實現可能對您的應用程序非常關鍵,以至於UAT中的應用程序的集成測試或手動檢查將暴露任何缺陷。
也許最終你會像這樣的東西(抱歉,我不說話斯卡拉所以它的Java):
public class RealConsoleService implements ConsoleService {
private final java.io.Console delegate;
public RealConsoleService(java.io.Console delegate) {
this.delegate = delegate;
}
@Override
public String readLine() throws IOError {
return delegate.readLine();
}
}
兩個有趣的點:
- 這就是爲什麼一個很好的例子測試驅動開發幫助編寫靈活的代碼。如果你想用另一種輸入輸出方法重寫你的框架,你只需將
ConsoleService
重命名爲更抽象的ApplicationInputOutputService
並插入不同的實現。
- 相同的概念可用於測試使用其他難以測試的API的應用程序。許多Java有用的文件IO方法都是靜態方法,因此很難在測試中進行控制。通過如上所述的界面包裝,您的應用程序功能變得易於測試。
100%的覆蓋率是在CS,亞伯拉罕林肯最大的幻想:) –
你有沒有考慮過查看SystemRules(http://stefanbirkner.github.io/system-rules/index.html),它的一套junit處理System類的規則。它可能會給你一些啓發。或者,ScalaCheck可以在這種情況下提供一些幫助,它是一種不同的測試方法。 – Gavin
@SleimanJneidi我毫不懷疑你在那裏是正確的! :)知道這是否有解決方案仍然很有趣,它似乎是Java中API設計的一個疏忽,我還沒有遇到過這樣的事情。 – Seer