2012-02-13 65 views
3

我正在使用Java 6和Mockito 1.8.5。我想嘲笑類的成員字段的方法,但我不知道如何。我有這些類...如何模擬類'member field的方法?

public class CacheService implements CacheCallback { 

private final Cache cache; 
... 

public static CacheService getInstance() { 
    return INSTANCE; 
} 

private CacheService() { 
    cache = new DefaultCacheImpl(); 
} 

public boolean saveNodes(final Map<Long, XmlNode> nodeMap) { 
    ... 
    cache.saveNodes(nodeMap); 
} 
... 
} 


public class DefaultCacheImpl implements Cache { 
... 
public void saveNodes(Map<Long, XmlNode> xmlNodes) { 
    dao.updateDB(xmlNodes); 
} 
... 
} 

我想不出如何嘲笑「緩存」成員字段的方法「saveNodes」。我嘲笑下面的方法,但是由於在該領域的CacheService類沒有setter,我無法弄清楚如何注入我的模擬..

public class PopulateCacheServiceImpl extends RemoteServiceServlet implements PopulateCacheService { 
... 
public Boolean initCache() { 
    boolean ret = false; 
    try { 
     setupMocks(); 
     CacheService.getInstance().startCache(); 
     PopulateCache.addTestEntriesToCache(); 
     ret = true; 
    } catch (Exception e) { 
     e.printStackTrace(System.err); 
     ret = false; 
    } // try 
    return ret; 
} // initCache 

private void setupMocks() { 
    DefaultCacheImpl cache = mock(DefaultCacheImpl.class); 
    doAnswer(new Answer<Object>() { 
     public Object answer(InvocationOnMock invocation) throws Throwable { 
      return null; 
     } 
    }).when(cache).saveNodes(Matchers.anyMap()); 
} // setupMocks 

} 

是否有任何其他的方式來做到這一點與Mockito?謝謝, - 戴夫

回答

3

的問題是在這條線:

cache = new DefaultCacheImpl(); 

如果您建造CacheService內部緩存對象,它們被緊密耦合。您不能將CacheService與另一個緩存實現一起使用。

相反,通過緩存實現的構造器:

public CacheService(Cache cacheImpl) { 
    this.cache = cacheImpl; 
} 

這使得CacheService使用任何的緩存實現。

+0

感謝這一點,但CacheService是一個單身人士,目前有一個私人的構造函數。我可以更改源代碼,但我仍然希望只有一個CacheService類的實例在jvm上浮動。 – Dave 2012-02-13 17:12:18

+2

@Dave你應該認真考慮避免對'CacheService.getInstance()'這個靜態依賴,並使用適當的DI,所以你不必在這裏和那裏破解一些東西來測試。你的設計也會更清潔和優雅。 – Brice 2012-02-13 18:14:54

+0

我接受你的意見,但我們的項目禁止使用第三方工具(如Spring)作爲核心項目(用於測試它的罰款)。我無法對抗我公司的政策。我如何設置DI,記住,我只想在JVM中使用CacheService對象的一個​​實例? – Dave 2012-02-13 18:39:20

1

如果您可以更改來源,decopule這些類。按照Sjoerd的建議從構造函數中刪除cache = new DefaultCacheImpl();

如果您不能使用PowerMock mock the constructorDefaultCacheImpl。我必須說,這是非常糟糕的解決方案(唯一的醜陋嘲弄靜態初始化代碼)。

注意: 您的代碼是一個流行問題「爲什麼需要依賴注入?」的答案。我認爲當人們發明DI時,他們正在尋找這樣的代碼。

+0

我喜歡PowerMock的想法b/c我不需要改變源代碼。我的問題是我必須使用「PrepareForTest」註釋嗎?上面的類聲明(「PopulateCacheServiceImpl」)不是JUnit測試類,而是通過許多不同的測試間接調用。 – Dave 2012-02-13 17:26:05

+0

是的,你必須做PrepareForTest註解。你也可以使用PowerMock來模擬靜態方法getInstance()並返回一個模擬而不是單例。我不認爲這有助於解決多個實例化點的問題。 – jhericks 2012-02-13 20:39:24

2

如何製作兩個構造函數?你擁有的那個會呆在那裏。另一個可以讓你傳遞Cache實現並允許你測試這個類。新的構造函數可以具有受保護的訪問權限,以限制哪些類可以使用它。

+2

或者包私有而不是保護。 – 2012-02-13 21:59:41