2009-07-29 119 views
15

如何輕鬆地嘲笑Java中的靜態方法?如何輕鬆地嘲笑Java中的靜態方法(jUnit4)

我使用Spring 2.5和JUnit 4.4

@Service 
public class SomeServiceImpl implements SomeService { 

    public Object doSomething() { 
     Logger.getLogger(this.class); //a static method invoked. 
     // ... 
    } 
} 

我不控制我的服務需要調用靜態方法,所以我不能重構它更單元測試。我以Log4J Logger爲例,但真正的靜態方法很相似。 這不是更改靜態方法的選項。

做Grails的工作,我已經習慣了使用類似:

def mockedControl = mockFor(Logger) 
mockControl.demand.static.getLogger{Class clazz-> … } 
… 
mockControl.verify() 

我該怎麼做在Java中類似的事情?

+0

你可以改變你的SomeServiceImpl實現嗎? – 2009-07-29 21:07:39

+0

沒關係,Jon Skeet剛剛發佈了我的想法。我感到自豪! (像Jon Skeet嘿嘿) – 2009-07-29 21:09:11

+0

是的,我可以改變SomeServiceImpl,但我爲什麼要?爲什麼額外的間接? – 2009-07-29 22:11:56

回答

0

基本上,Java + Spring 2.5中沒有簡單的方法來做到這一點。JUnit 4.4目前。

儘管可以重構並抽象出靜態調用,但重構代碼並不是我尋找的解決方案。

JMockit看起來好像可以工作,但與Spring 2.5和JUnit 4.4不兼容。

14

你的意思是你不能控制調用代碼?因爲如果你控制調用靜態方法,但不是實現本身,你可以很容易地進行測試。使用與靜態方法相同的簽名的單個方法創建依賴性接口。你的生產實現只是調用靜態方法,但是當前調用靜態方法的任何東西都會通過接口調用。

然後,您可以以正常方式嘲笑該界面。

1
public interface LoggerWrapper { 
    public Logger getLogger(Class<?> c); 
    } 
public class RealLoggerWrapper implements LoggerWrapper { 
    public Logger getLogger(Class<?> c) {return Logger.getLogger(c);} 
    } 
public class MockLoggerWrapper implements LoggerWrapper { 
    public Logger getLogger(Class<?> c) {return somethingElse;} 
    } 
0

這就是靜態方法不好的原因之一。

我們重新設計了我們大多數工廠的設置,以便我們可以將模擬對象設置到其中。事實上,我們想出了一些與依賴注入相近的東西,其中一個方法就像我們所有的單例一樣充當工廠。

在你的情況下,添加一個Logger.setLogger()方法(並存儲該值)可以工作。如果您不得不擴展記錄器類並使用自己的getLogger方法。

4

JMockit框架承諾允許模擬靜態方法。

https://jmockit.dev.java.net/

事實上,它使一些相當大膽的索賠,包括靜態方法是非常有效的設計選擇和它們的使用不應該因爲測試框架的不足之處加以限制。

無論這些聲明是否合理,JMockit框架本身都非常有趣,儘管我還沒有親自嘗試。

0

您可以使用AspectJ攔截靜態方法調用併爲您的測試執行一些有用的操作。

1

作爲上面的另一個回答,JMockit可以嘲笑靜態方法(以及其他任何其他)。

它甚至直接支持日誌框架。例如,您可以編寫:


@UsingMocksAndStubs(Log4jMocks.class) 
public class SomeServiceTest 
{ 
    // All test methods in this class will have any calls 
    // to the Log4J API automatically stubbed out. 
} 

但是,對JUnit 4.4的支持已經丟失。支持JUnit 3.8,JUnit 4.5+和TestNG 5.8+。

4

PowerMock有此能力。它也可以模擬內部對象的實例化。如果您測試的方法調用新的Foo(),則可以爲該Foo創建一個模擬對象,並將其替換爲您正在測試的方法。

像抑制構造函數和靜態初始值設定項也是可能的。所有這些東西都被認爲是不可測試的代碼,因此不建議這樣做,但如果您有遺留代碼,更改它並不總是一種選擇。如果你在那個位置,PowerMock可以幫助你。