I'm developing a simple cache functionality with EhCache. There is a generic base class that implements my cache interface (BECache):
public class EhCacheBase<K, V> implements BECache<K, V> {
private static CacheManager cacheManager;
private String cacheName;
public EhCacheBase(String cacheName) {
this.cacheName = cacheName;
}
public void cache(K key, V value) {
Cache cache = cacheManager.getCache(cacheName);
if (cache == null) {
throw new NullPointerException("Failed to obtain cache: " + cacheName);
}
Element element = new Element(key, value);
cache.put(element);
}
The purpose is to have caches defined as follows:
public class ObjectCache extends EhCacheBase<String, Object>{
public ObjectCache (String cacheName) {
super(cacheName);
}
}
assuming 'cacheName' is defined in ehcache.xml I get a nice class for caching certain objects.
I'm trying to write a test for the 'EhCacheBase.cache(...)' method that ensures a cache is retrieved and a value is put (using Mockito):
private static final String CACHE_NAME = "testCache";
@Mock
private CacheManager cacheManager;
@Mock
private Cache cache;
private EhCacheBase<String, Object> ehCacheBase;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
ehCacheBase = new EhCacheBase<String, Object>(CACHE_NAME);
EhCacheBase.setCacheManager(cacheManager);
}
@Test
public void shouldRetrieveCacheAndPutOneValueInIt() {
//given
Object o = new Object();
when(cacheManager.getCache(CACHE_NAME)).thenReturn(cache);
//when
ehCacheBase.cache("KEY", o);
//then
verify(cacheManager, times(1)).getCache(CACHE_NAME);
verify(cache, times(1)).put(any(Element.class));
}
When I run the test I get:
java.lang.NullPointerException
at net.sf.ehcache.Cache.checkStatus(Cache.java:2731)
at net.sf.ehcache.Cache.putInternal(Cache.java:1440)
at net.sf.ehcache.Cache.put(Cache.java:1417)
at net.sf.ehcache.Cache.put(Cache.java:1382)
at org.fxoo.bookingengine.dao.dataproviders.caches.EhCacheBase.cache(EhCacheBase.java:24)
at org.fxoo.bookingengine.dao.dataproviders.caches.EhCacheBaseTest.shouldCacheOneValue(EhCacheBaseTest.java:41)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:35)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:115)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.maven.surefire.booter.ProviderFactory$ClassLoaderProxy.invoke(ProviderFactory.java:103)
at $Proxy0.invoke(Unknown Source)
at org.apache.maven.surefire.booter.SurefireStarter.invokeProvider(SurefireStarter.java:150)
at org.apache.maven.surefire.booter.SurefireStarter.runSuitesInProcess(SurefireStarter.java:91)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:69)
It seems like the 'put' method in 'cache' mock is calling some others methods. But as you can read in Mockito docs:
Use doNothing() for setting void methods to do nothing. Beware that void methods on mocks do nothing by default! However, there are rare situations when doNothing() comes handy:
By the way, I tried: doNothing().when(cache).put(any(Element.class)); and it didn't help either.
Any ideas what is going on?
Cache.put is final, so you need to use PowerMock if you want to mock it.
Your test would look like this:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Cache.class)
public class EhCacheBaseTest {
private static final String CACHE_NAME = "testCache";
private CacheManager cacheManager;
private Cache cache;
private EhCacheBase<String, Object> ehCacheBase;
@Before
public void init() {
// You can statically import mock method, but left it this way
// for clarity
cacheManager = PowerMockito.mock(CacheManager.class);
cache = PowerMockito.mock(Cache.class);
ehCacheBase = new EhCacheBase<String, Object>(CACHE_NAME);
EhCacheBase.setCacheManager(cacheManager);
}
@Test
public void shouldRetrieveCacheAndPutOneValueInIt() {
//given
Object o = new Object();
when(cacheManager.getCache(CACHE_NAME)).thenReturn(cache);
//when
ehCacheBase.cache("KEY", o);
//then
verify(cacheManager, times(1)).getCache(CACHE_NAME);
verify(cache, times(1)).put(any(Element.class));
}
}
I posted a similar question to Mockito Google Group (here). I got a quick response from Eric Lefevre-Ardant that explained everything. The net.sf.ehcache.Cache.put method is final so it cannot be stubbed - Mockito fails to provide its own implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With