简体   繁体   中英

How do I mock a method of a class' member field?

I'm using Java 6 and Mockito 1.8.5. I want to mock a class' member field's method, but I can't figure out how. I have these classes ...

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);
}
...
}

I can't figure out how to mock the "cache" member field's method "saveNodes". I'm mocking the method below, but because there is no setter in the CacheService class for the field, I can't figure out how to inject my mock ..

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 

}

Are there any other ways to do this with Mockito? Thanks, - Dave

The problem is in this line:

cache = new DefaultCacheImpl();

If you construct a cache object inside your CacheService, they are tightly coupled. You can not use the CacheService with another cache implementation.

Instead, pass the cache implementation to the constructor:

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

This allows the CacheService to use any Cache implementation.

What about making two constructors? The one you have would stay there. Another one would let you pass in the Cache implementation and allow you to test the class. The new constructor can have protected access to limit which classes can use it.

If you can change the source, decopule those classes. Get rid of cache = new DefaultCacheImpl(); from constructor as Sjoerd suggested.

If you can't - use PowerMock to mock the constructor of DefaultCacheImpl . I must say that this is really ugly solution (the only uglier is mocking static initialization code).

Note: Your code is an answer to popular question "Why do I need dependency injection for?". I think people were looking at code like this when they invented DI.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM