Last active
March 25, 2021 17:48
-
-
Save philmadden83/2726a334ce52e7181a5d to your computer and use it in GitHub Desktop.
Unit test with automatic singleton mocking and wiring
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.vivid.publik; | |
import com.ibatis.dao.client.DaoManager; | |
import com.vivid.estore.persistence.DaoConfig; | |
import com.vivid.estore.persistence.iface.*; | |
import com.vivid.estore.persistence.sqlmapdao.*; | |
import com.vivid.estore.util.TestFixtureFactory; | |
import org.mockito.Mock; | |
import org.mockito.Mockito; | |
import org.mockito.MockitoAnnotations; | |
import org.powermock.core.classloader.annotations.PrepareForTest; | |
import org.powermock.core.classloader.annotations.SuppressStaticInitializationFor; | |
import org.powermock.api.mockito.PowerMockito; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationTargetException; | |
import java.lang.reflect.Method; | |
import static org.mockito.Mockito.eq; | |
/** | |
* Created by Phil Madden on 10/3/14. | |
*/ | |
@SuppressStaticInitializationFor("com.vivid.estore.persistence.DaoConfig") | |
@PrepareForTest({ DaoConfig.class }) | |
public abstract class BaseUnitTest { | |
@Mock | |
private static final DaoManager mockDaoManager = Mockito.mock(DaoManager.class); | |
private static boolean initialized = false; | |
protected static final TestFixtureFactory testFixtureFactory = new TestFixtureFactory(); | |
public BaseUnitTest() | |
{ | |
if(!initialized) | |
setup(); | |
} | |
public abstract void before(); | |
public abstract void after(); | |
public final void setup() | |
{ | |
MockitoAnnotations.initMocks(this); | |
mockServices(); | |
mockDaoManagers(); | |
before(); | |
initialized = true; | |
} | |
public final void mockDaoManagers() | |
{ | |
PowerMockito.mockStatic(DaoConfig.class); | |
PowerMockito.when(DaoConfig.getDaomanager()).thenReturn(mockDaoManager); | |
Mockito.when(mockDaoManager.getDao(eq(AccountDao.class))).thenReturn(Mockito.mock(AccountSqlMapDao.class)); | |
Mockito.when(mockDaoManager.getDao(eq(AlertDao.class))).thenReturn(Mockito.mock(AlertSqlMapDao.class)); | |
Mockito.when(mockDaoManager.getDao(eq(AttributeDao.class))).thenReturn(Mockito.mock(AttributeSqlMapDao.class)); | |
Mockito.when(mockDaoManager.getDao(eq(AuditDao.class))).thenReturn(Mockito.mock(AuditSqlMapDao.class)); | |
Mockito.when(mockDaoManager.getDao(eq(AuthorizationDao.class))).thenReturn(Mockito.mock(AuthorizationSqlMapDao.class)); | |
Mockito.when(mockDaoManager.getDao(eq(BrokerDao.class))).thenReturn(Mockito.mock(BrokerSqlMapDao.class)); | |
} | |
private void mockServices() { | |
if(getClass().getAnnotation(PrepareForTest.class) != null) { | |
PrepareForTest singletons = getClass().getAnnotation(PrepareForTest.class); | |
//For each class declared in the @PrepareForTest annotation | |
for(Class serviceClass : singletons.value()) { | |
try { | |
//Get the service's instance method | |
Method instanceMethod = serviceClass.getDeclaredMethod("getInstance"); | |
PowerMockito.mockStatic(serviceClass); | |
//Find a instance field in the test class that references the service class | |
for (Field mockedServiceField : this.getClass().getDeclaredFields()) { | |
if (mockedServiceField.getType() == serviceClass) { | |
//Setup and inject the mock | |
Object mockedService = PowerMockito.mock(mockedServiceField.getType()); | |
if (!mockedServiceField.isAccessible()) | |
mockedServiceField.setAccessible(true); | |
try { | |
mockedServiceField.set(this, mockedService); | |
PowerMockito.when(instanceMethod.invoke(mockedService)).thenReturn(mockedService); | |
} catch (IllegalAccessException | InvocationTargetException e) { | |
e.printStackTrace(); | |
} | |
break; | |
} | |
} | |
} catch (NoSuchMethodException e) { | |
e.printStackTrace(); | |
} | |
} | |
} | |
} | |
public final void teardown() | |
{ | |
after(); | |
} | |
} | |
A TEST | |
============================================================================================================== | |
package com.vivid.publik; | |
import com.vivid.estore.domain.Ticket; | |
import com.vivid.estore.service.EventService; | |
import com.vivid.estore.service.FeedService; | |
import org.junit.Test; | |
import org.junit.runner.RunWith; | |
import org.powermock.core.classloader.annotations.PrepareForTest; | |
import org.powermock.modules.junit4.PowerMockRunner; | |
import static org.junit.Assert.assertNotNull; | |
import static org.junit.Assert.assertTrue; | |
import static org.mockito.Matchers.anyInt; | |
import static org.mockito.Matchers.anyString; | |
import static org.mockito.Mockito.when; | |
/** | |
* Created by Phil Madden on 10/3/14. | |
*/ | |
@RunWith(PowerMockRunner.class) | |
@PrepareForTest({ FeedService.class, EventService.class}) | |
public class TestEst extends BaseUnitTest { | |
private static FeedService mockFeedService; | |
private static EventService mockEventService; | |
@Override | |
public void before() { | |
when(mockFeedService.getItem(anyString(), anyInt())).thenReturn(testFixtureFactory.getTicket()); | |
when(mockEventService.getCategoryContent(anyInt())).thenReturn("I'm Mocked!"); | |
} | |
@Override | |
public void after() { | |
} | |
@Test | |
public void testFeedService() | |
{ | |
Ticket t = mockFeedService.getItem("", 1); | |
assertNotNull(t); | |
} | |
@Test | |
public void testEventService() | |
{ | |
String output = mockEventService.getCategoryContent(1); | |
assertTrue(output.equals("I'm Mocked!")); | |
} | |
} |
It removes the boilerplate code below for each singleton you need to mock in a unit test, which could be a lot.
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance()).thenReturn(mock(Singleton.class));
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The "mockServices" method in the BaseUnitTest class is reflecting the test thats about to run. It automatically mocks a singleton, wires up the "when singleton.getInstance return my mock object" code and injects the mock into the test's instance field.
effectively "mockedFeedService" and "mockedEventService" will be automatically wired up to a mocked version of the service class implementation. Works pretty well.
You can still use EventService.getInstance().someMethod though. You don't have to use the instance field.