2012-02-13 93 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