Class MockComponentWorker
- All Implemented Interfaces:
ComponentAccessor.Worker
ComponentAccessor to return mock
instances of components for unit testing.
When a class needs to access another JIRA component, the preferred mechanism to resolve this dependency is by injection in the constructor. Among other things, this makes it easier to see what the dependencies of the class are and also makes it easy for unit tests to provide mocks for those components. However, there are times when dependency injection is impossible or impractical. Examples include:
- Components with circular dependencies between them, as one of them must be resolved first without the other dependency available for injection yet
- Classes that are not injectable components but are instead explicitly constructed in a long chain of classes that would not otherwise need the target component
- Static-only utility classes, which are never constructed at all
In these cases, the class can use ComponentAccessor.getComponentOfType(Class)
to resolve the dependency, instead. The drawback is that ComponentAccessor
uses a global, static reference to a ComponentAccessor.Worker implementation
to accomplish this, and if nothing has initialised that reference, then an
IllegalStateException is thrown.
Unit tests must be responsible for ensuring that everything they require, directly or indirectly, is arranged during the test's setup, so a unit test that encounters this problem should explicitly initialise the component accessor. In most cases, all they need to do is create and install an instance of this class. For example:
@Before
public void setUp()
{
new MockComponentWorker().init();
}
The MockComponentWorker comes with a few mocks by default, including
implementations for common problem areas, such as the UserKeyService and
the UserPreferencesManager, so for many tests this will be enough as-is.
If you need additional mocked components to be resolved in this way, then you can
add them as well. An example might look something like this:
@Before
public void setUp()
{
final ApplicationUser fred = new MockApplicationUser("Fred");
final JiraAuthenticationContext jiraAuthenticationContext = Mockito.mock(JiraAuthenticationContext.class);
Mockito.when(jiraAuthenticationContext.getUser()).thenReturn(fred);
Mockito.when(jiraAuthenticationContext.getUser()).thenReturn(fred.getDirectoryUser());
new MockComponentWorker()
.addMock(ConstantsManager.class, new MockStatusConstantsManager())
.addMock(JiraAuthenticationContext.class, jiraAuthenticationContext)
.init();
}
JUnit 4 annotations can also be used to initialise the MockComponentWorker.
See the
MockComponentContainer
and
MockitoContainer
@Rules for examples.
- Since:
- v4.4
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription<T,U extends T>
MockComponentWorkeraddMock(Class<T> componentInterface, io.atlassian.util.concurrent.LazyReference<U> componentMockRef) Registers a mock component to be returned by this component worker.<T,U extends T>
MockComponentWorkerRegisters a mock component to be returned by this component worker.<T> TgetComponent(Class<T> componentClass) Obtains the registered mock of the specified interface type.<T> TgetComponentOfType(Class<T> componentClass) Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponent(Class).<T> Optional<T>getComponentSafely(Class<T> componentClass) This implementation just wraps the result ofgetComponent(Class)withOptional.ofNullable(Object).<T> TgetOSGiComponentInstanceOfType(Class<T> componentClass) Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponent(Class).<T> Optional<T>getOSGiComponentInstanceOfTypeSafely(Class<T> componentClass) Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponentSafely(Class).init()Convenience method that just callsComponentAccessor.initialiseWorker(this).<T,U extends T>
voidregisterMock(Class<T> componentInterface, U componentMock) Registers a mock component to be returned by this component worker.
-
Constructor Details
-
MockComponentWorker
public MockComponentWorker()
-
-
Method Details
-
registerMock
Registers a mock component to be returned by this component worker.Since
addMock(Class, Object)is identical but also returnsthisfor call chaining, it may be more convenient to use.- Type Parameters:
T- thecomponentInterfaceU- thecomponentMock's class, which must implement<T>- Parameters:
componentInterface- the interface that the mock component must implementcomponentMock- the component that implements the interface- See Also:
-
addMock
Registers a mock component to be returned by this component worker.This method is exactly equivalent to
registerMock(Class, Object), except that it also returnsthis, making it possible to use it in the convenient call chaining style illustrated in the documentation for this class.- Type Parameters:
T- thecomponentInterfaceU- thecomponentMock's class, which must implement<T>- Parameters:
componentInterface- the interface that the mock component must implementcomponentMock- the component that implements the interface- Returns:
this, for convenience
-
addMock
public <T,U extends T> MockComponentWorker addMock(Class<T> componentInterface, io.atlassian.util.concurrent.LazyReference<U> componentMockRef) Registers a mock component to be returned by this component worker.This method accepts a lazy reference to the implementation in order to allow lazy initialisation on first access.
- Type Parameters:
T- thecomponentInterfaceU- thecomponentMock's class, which must implement<T>- Parameters:
componentInterface- the interface that the mock component must implementcomponentMockRef- the lazy reference to the component that implements the interface- Returns:
this, for convenience
-
getComponentSafely
This implementation just wraps the result ofgetComponent(Class)withOptional.ofNullable(Object).- Specified by:
getComponentSafelyin interfaceComponentAccessor.Worker- Type Parameters:
T- the return type inferred from the specifiedcomponentClass.- Parameters:
componentClass- the interface for which an implementation is desired- Returns:
- the requested component (wrapped in an
Optional), orOptional.empty()if no mock implementation has been provided for it.
-
getComponent
Obtains the registered mock of the specified interface type. Test code would normally only need to call this to retrieve access to mocks that were provided automatically (such as theUserKeyService) or that the test registered earlier and is now ready to stub.JIRA itself will also sometimes use this method (or
getComponentOfType(Class)) to resolve dependencies. Arguably, this method should throw an exception rather than returnnullwhen a component is requested without a mock provided for it; however, some unit tests will resolve the component during construction without the test code path actually needing to use it, so this method just generates a warning message in the log for this case.- Specified by:
getComponentin interfaceComponentAccessor.Worker- Type Parameters:
T- the return type inferred from the specifiedcomponentClass.- Parameters:
componentClass- the interface for which an implementation is desired- Returns:
- the requested component, or
nullif no mock implementation has been provided for it.
-
getComponentOfType
Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponent(Class).- Specified by:
getComponentOfTypein interfaceComponentAccessor.Worker- Type Parameters:
T- as forgetComponent(Class)- Parameters:
componentClass- as forgetComponent(Class)- Returns:
- as for
getComponent(Class)
-
getOSGiComponentInstanceOfType
Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponent(Class).- Specified by:
getOSGiComponentInstanceOfTypein interfaceComponentAccessor.Worker- Type Parameters:
T- as forgetComponent(Class)- Parameters:
componentClass- as forgetComponent(Class)- Returns:
- as for
getComponent(Class)
-
getOSGiComponentInstanceOfTypeSafely
Although theComponentAccessorspecifies different semantics for this method, in this mock implementation it behaves identically togetComponentSafely(Class).- Specified by:
getOSGiComponentInstanceOfTypeSafelyin interfaceComponentAccessor.Worker- Type Parameters:
T- the return type inferred from the specifiedcomponentClass.- Parameters:
componentClass- the interface for which an implementation is desired- Returns:
- the requested component (wrapped in an
Optional), orOptional.empty()if no mock implementation has been provided for it.
-
init
Convenience method that just callsComponentAccessor.initialiseWorker(this). If you are developing a plugin that must support JIRA versions prior to v6.0, then that @Internal method must be used directly, instead.- Since:
- v6.0
-
getMockUserPreferences
-
getMockApplicationProperties
-
getMockUserKeyService
-
getMockDatabaseConfigurationService
-