The Master Controller code splits its core functionality between "managers" with specific responsibilities, including handling boundary crossings to the View and to the Model/Model Controller. Each manager is housed in a container class. This is an excellent spot to introduce dependency injection for test components, as shown in the ManagerControllerSample.java below:
This file contains 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
public class ManagerControllerSample { | |
@Inject protected GuiServerMgr _guiServerMgr; | |
@Inject protected SacMgr _sacMgr; | |
@Inject protected DomainMgr _domainMgr; | |
@Inject protected MessageMgr _messageMgr; | |
@Inject protected NavigationMgr _naviMgr; | |
List<IManager> _managerList = new ArrayList<IManager>(); | |
public void init(IPlatform iPlatform) { | |
myContainer.graph().inject(this); | |
_managerList.add(_guiServerMgr); | |
_managerList.add(_messageMgr); | |
_managerList.add(_domainMgr); | |
_managerList.add(_sacMgr); | |
_managerList.add(_naviMgr); | |
... | |
} | |
} |
This file contains 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
public class MyContainer { | |
private Graph _daggerGraph; | |
public MyContainer() { | |
initGraph(EnumSet.noneOf(IManager.ManagerType.class), EnumSet.noneOf(IManager.ManagerType.class)); | |
} | |
public Graph graph() { return _daggerGraph; } | |
public void initGraph(EnumSet<IManager.ManagerType> mockedManagers, EnumSet<IManager.ManagerType> spiedManagers) { | |
_daggerGraph = Graph.Initializer.init(mockedManagers, spiedManagers); | |
} | |
} |
I created a test framework that allowed for each integration test to determine for each manager whether it would be implemented as a mock, using the standard implementation, or using a spied version of the standard implementation. With Dagger2, I was able to inject the appropriate component (test or production) at run-time. In Graph.java we see the dagger graph. IntegrationTestBase.java specifies the list of managers to mock and spy as seen in the call to initGraph(), and ManagerTestDataModule injects the appropriate manager type (standard, mock, or spy):
This file contains 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
@Singleton | |
@Component(modules = {ManagerTestDataModule.class}) | |
public interface Graph { | |
void inject(ManagerController managerController); | |
public final static class Initializer { | |
public static Graph init(EnumSet<IManager.ManagerType> mockedManagers, EnumSet<IManager.ManagerType> spiedManagers) { | |
return DaggerGraph.builder() | |
.managerTestDataModule(new ManagerTestDataModule(mockedManagers, spiedManagers)) | |
.build(); | |
} | |
} | |
} |
This file contains 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
public class IntegrationTestBase { | |
@Inject protected GuiServerMgr _guiServerMgr; | |
@Inject protected SacMgr _sacMgr; | |
@Inject protected DomainMgr _domainMgr; | |
@Inject protected MessageMgr _messageMgr; | |
private MyContainer _container; | |
@Before | |
public void setup() { | |
MockitoAnnotations.initMocks(this); | |
_container = new MyContainer(); | |
_container.initGraph(getMockedManagers(), getSpiedManagers()); | |
_container.graph().inject(this); | |
//... | |
} | |
//override in tests to setup mocks | |
protected EnumSet<IManager.ManagerType> getMockedManagers() { | |
return EnumSet.noneOf(IManager.ManagerType.class); | |
} | |
//override in tests to setup spies | |
protected EnumSet<IManager.ManagerType> getSpiedManagers() { | |
return EnumSet.noneOf(IManager.ManagerType.class); | |
} | |
} |
This file contains 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
@Module | |
public final class ManagerTestDataModule { | |
private final EnumSet<IManager.ManagerType> _mockedManagers; | |
private final EnumSet<IManager.ManagerType> _spiedManagers; | |
public ManagerTestDataModule(EnumSet<IManager.ManagerType> mockedManagers, EnumSet<IManager.ManagerType>spiedManagers) { | |
_mockedManagers = mockedManagers; | |
_spiedManagers = spiedManagers; | |
} | |
@Provides @Singleton | |
GuiServerMgr provideServerMgr() { | |
if (_mockedManagers.contains(IManager.ManagerType.Server)) { | |
return mock(GuiServerMgr.class); | |
} else if (_spiedManagers.contains(IManager.ManagerType.Server)) { | |
return spy(new GuiServerMgr()); | |
} | |
return new GuiServerMgr(); | |
} | |
//... | |
} |