2014-04-18 59 views
20

什麼是通過此測試必須完成的絕對最小嘲弄?PowerMock:模擬出私有靜態最終變量,一個具體示例

代碼:

class PrivateStaticFinal { 
    private static final Integer variable = 0; 
    public static Integer method() { return variable + 1; } 
} 

測試:

@RunWith(PowerMockRunner.class) 
@PrepareForTest(PrivateStaticFinal.class) 
class PrivateStaticFinalTest { 
    @Test 
    public void testMethod() { 
     //TODO PrivateStaticFinal.variable = 100 
     assertEquals(PrivateStaticFinal.method(), 101); 
    } 
} 

相關:Mock private static final variables in the testing class(沒有明確的答案)

+1

這些靜態都在同一類,這使得它很棘手。我遇到的所有問題都是在嘗試測試課程時使用來自其他地方的靜態信息。不得不嘲笑你想要測試的類的部分部分似乎很糟糕。你永遠不應該嘲笑你正在測試的類,只是它依賴的東西。 – Walls

回答

34

免責聲明:很多狩獵後圍繞各個線程我發現一個答案。這是可以做到的,但一般的共識是,這是不是很安全,但看到你是如何做只在單元測試,我想你接受這些風險:)


答案是不是嘲笑,因爲大多數Mocking不允許你進入決賽。答案有點「哈克」,當Java調用時,實際上修改私有字段的核心是核心java.lang.reflect.Fieldjava.lang.reflect.Modifier類(反射)。看着this answer我能夠拼湊出剩下的測試,而不需要嘲笑解決你的問題。

該答案的問題是我試圖修改variable時遇到NoSuchFieldException。對此的幫助在於another post關於如何訪問私有而非公開的字段。

反射/域操作說明:

由於懲戒不能處理的最後,而不是我們最終做的是黑客進入該領域本身的根源。當我們使用Field操作(反射)時,我們正在尋找類/對象內的特定變量。一旦Java找到它,我們就會得到它的「修飾符」,它告訴變量它有什麼限制/規則,像final,static,private,public等等。我們找到正確的變量,然後告訴代碼它可以被訪問允許我們改變這些修飾符。一旦我們改變了根的「訪問」以允許我們操縱它,我們就切換了它的「最終」部分。然後我們可以改變它的價值並將其設置爲我們需要的任何東西。簡而言之,我們正在修改變量以允許我們更改其屬性,刪除final的價格,然後更改價值,因爲它不再是final。欲瞭解更多信息,請致電check out the post where the idea came from

那麼一步,我們在我們要處理的變量傳遞步驟和...

// Mark the field as public so we can toy with it 
field.setAccessible(true); 
// Get the Modifiers for the Fields 
Field modifiersField = Field.class.getDeclaredField("modifiers"); 
// Allow us to change the modifiers 
modifiersField.setAccessible(true); 
// Remove final modifier from field by blanking out the bit that says "FINAL" in the Modifiers 
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
// Set new value 
field.set(null, newValue); 

結合這一切變成一個新的超級回答你。

@RunWith(PowerMockRunner.class) 
@PrepareForTest() 
class PrivateStaticFinalTest { 
    @Test 
    public void testMethod(){ 
     try { 
     setFinalStatic(PrivateStaticFinal.class.getDeclaredField("variable"), Integer.valueOf(100)); 
     } 
     catch (SecurityException e) {fail();} 
     catch (NoSuchFieldException e) {fail();} 
     catch (Exception e) {fail();} 
     assertEquals(PrivateStaticFinal.method(), Integer.valueOf(101)); 
    } 

    static void setFinalStatic(Field field, Object newValue) throws Exception { 
     field.setAccessible(true); 
     // remove final modifier from field 
     Field modifiersField = Field.class.getDeclaredField("modifiers"); 
     modifiersField.setAccessible(true); 
     modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); 
     field.set(null, newValue); 
    } 
} 

更新 上述解決方案將只工作了這是在靜態初始化block.When聲明,並在同一時間初始化常數這些常數,它可以發生,編譯器內聯的,在這一點任何對原始值的改變都會被忽略。

+0

你能解釋一下什麼'Field modifiersField = Field.class.getDeclaredField(「modifiers」); modifiersField.setAccessible(true); modifierField.setInt(field,field.getModifiers()&〜Modifier.FINAL); field.set(null,newValue); '確實? –

+0

編輯了一些答案,做了一些更多的解釋,以及原始文章的鏈接,該文章是「修改」「修飾符」以允許我們侵入變量本身並移除最終性的想法的來源的。 – Walls

+0

謝謝!我想測試代碼仍然尊重源代碼,所以將'modifiersField.setInt(field,field.getModifiers()&Modifier.FINAL); modifiersField.setAccessible(false);'在我們設置了我們想要的值之後工作。即「做/撤銷」的事情。 –