我于1995年夏季首次閱讀Grady Booch的1994年的《 面向對象的分析和設計 》一書,它通過以下方式定義了對象的狀態:
對象的狀態包含對象的所有(通常是靜態)屬性以及這些屬性中每個屬性的當前(通常是動態)值。
他使用自動售貨機示例定義了靜態和動態狀態之間的差異。 靜態狀態是通過機器始終準備好取錢的方式表現出來的,而動態狀態是在任何給定實例中獲得多少錢的狀態。
我懷疑在這一點上,您會正確地辯稱,顯式行為測試確實會測試對象的狀態,這是因為給定的方法調用返回了正確的結果,并且為了獲得正確的結果,對象的狀態也必須是是的...我會同意的。 但是,在極少數情況下,經典行為測試不適用。 當公共方法調用沒有輸出并且對對象不執行任何操作(更改其狀態)時,會發生這種情況。 一個示例是返回void的方法或構造函數。 例如,給定一個具有以下簽名的方法:
public void init();
…您如何確保已完成工作? 事實證明,有幾種方法可以用來實現這一目標……
- 在您的類中添加許多getter方法。 這并不是一個特別好的主意,因為您只是在松開后門的封裝。
- 放松封裝:將私有實例變量打包為私有。 一個非常有爭議的事情。 您可能會務實地認為,經過良好測試,正確和可靠的代碼可能比具有高度的封裝更好,但是我在這里不太確定。 這可能是一個短期修復,但將來可能會導致各種問題,因此應該有一種編寫經過良好測試,正確和可靠的代碼的方式,其中不包括破壞對象的封裝
- 編寫一些使用反射來訪問對象內部狀態的代碼。 這是迄今為止最好的主意。 不利的一面是,這需要付出相當大的努力,并且需要一定數量的編程能力。
- 使用PowerMock的Whitebox測試課程為您完成艱苦的工作。
以下完全人為設計的方案演示了PowerMock的Whitebox類的用法。 它需要一個非常簡單的AnchorTag <a>類,該類將在測試輸入URL字符串有效之后構建一個錨標記。
public class AnchorTag {private static final Logger logger = LoggerFactory.getLogger(AnchorTag.class);/** Use the regex to figure out if the argument is a URL */private final Pattern pattern = Pattern.compile("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$");/*** A public method that uses the private method*/public String getTag(String url, String description) {validate(url, description);String anchor = createNewTag(url, description);logger.info("This is the new tag: " + anchor);return "The tag is okay";}/*** A private method that's used internally, but is complex enough to require testing in its own right*/private void validate(String url, String description) {Matcher m = pattern.matcher(url);if (!m.matches()) {throw new IllegalArgumentException();}}private String createNewTag(String url, String description) {return "<a href=\"" + url + "\">" + description + "</a>";}
}
URL驗證測試是使用正則表達式和Java Pattern對象完成的。 使用Whitebox類將確保正確配置模式對象,并且我們的AnchorTag處于正確的狀態。 下面的JUnit測試證明了這一點:
/*** Works for private instance vars. Does not work for static vars.*/@Testpublic void accessPrivateInstanceVarTest() throws Exception {Pattern result = Whitebox.<pattern> getInternalState(instance, "pattern");logger.info("Broke encapsulation to get hold of state: " + result.pattern());assertEquals("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$", result.pattern());}
該測試的關鍵是:
Pattern result = Whitebox.<pattern> getInternalState(instance, "pattern");
…使用反射返回Pattern對象的私有實例變量。 一旦可以訪問該對象,我們只需調用它即可詢問它是否已正確初始化:
assertEquals("^([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}$", result.pattern());
總而言之,我建議僅在無法使用簡單直接的經典JUnit測試進行行為測試時,才應使用PowerMock顯式測試對象的內部狀態。 話雖如此,它是工具箱中的另一個工具,可以幫助您編寫更好的代碼。
參考:來自JCG合作伙伴的 PowerMock測試對象的內部狀態 調試隊長博客上的 Roger。
- JUnit 4.9(測試版3)中的規則
- Servlet 3.0異步處理可將服務器吞吐量提高十倍
- 用Scala測試
- Java工具:源代碼優化和分析
- Java教程和Android教程列表
翻譯自: https://www.javacodegeeks.com/2011/10/testing-objects-internal-state-with.html