Reputation: 161
I'm struggling with mocking (using Mockito) the DefaultMessageListenerContainer
(org.springframework.jms.listener.DefaultMessageListenerContainer
). Here is my code:
@Mock
private DefaultMessageListenerContainer defaultMessageListenerContainer;
@Before
public void init() {
MockitoAnnotations.initMocks( this );
incomingFeedController = new IncomingFeedControllerImpl();
}
@Test
public void testHandleConnectionState() {
List< DefaultMessageListenerContainer > listeners =
new ArrayList< DefaultMessageListenerContainer >();
listeners.add( defaultMessageListenerContainer );
incomingFeedController.setContainers( listeners );
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
}
Then I would like to do some proper tests like:
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
But after junit run this line end up with:
java.lang.NullPointerException at org.springframework.jms.listener.AbstractJmsListeningContainer.isRunning(AbstractJmsListeningContainer.java:312) at com.source.etf.manager.integrationgateway.feedcontroller.IncomingFeedControllerTest.testHandleConnectionState(IncomingFeedControllerTest.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79) at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87) at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77) at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42) at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88) at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51) at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44) at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27) at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37) at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
I also checked the AbstractJmsListeningContainer
and here is the code where NPE occures:
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return (this.running && runningAllowed());
}
}
What I found is that the lifecycleMonitor
object is not being instantiated. This object is declared in AbstractJmsListeningContainer
just at the top:
protected final Object lifecycleMonitor = new Object();
Any idea how to properly mock DefaultMessageListenerContainer
?
Upvotes: 6
Views: 4481
Reputation: 1
private DefaultMessageListenerContainer createFailingContainer() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer() {
@Override
public void start() throws JmsException {
throw new MyJmsException("TEST");
}
@Override
public void stop() throws JmsException {
throw new MyJmsException("TEST");
}
};
return container;
}
class MyJmsException extends JmsException {
private static final long serialVersionUID = 1L;
public MyJmsException(String msg) {
super(msg);
}
}
Upvotes: 0
Reputation: 5971
You will either need to use PowerMock, or to do an integration test as @Brice suggests. Here's how to do it in PowerMock:
@RunWith(PowerMockRunner.class)
@PrepareForTest(DefaultMessageListenerContainer.class)
public class MyTestClass {
// cannot use the @Mock annotation
private DefaultMessageListenerContainer defaultMessageListenerContainer;
@Before
public void init() {
// This should allow the mocking of final methods as well.
defaultMessageListenerContainer =
PowerMockito.mock(DefaultMessageListenerContainer.class);
incomingFeedController = new IncomingFeedControllerImpl();
}
@Test
public void testHandleConnectionState() {
List< DefaultMessageListenerContainer > listeners =
new ArrayList< DefaultMessageListenerContainer >();
listeners.add( defaultMessageListenerContainer );
incomingFeedController.setContainers( listeners );
when( defaultMessageListenerContainer.isRunning() ).thenReturn( false );
}
}
Upvotes: 0
Reputation: 42283
Mockito can't mock final classes or final methods. These constraints are enforced by the JVM itself. Mocking such code would requite to actually rewrite the class bytecode and load it in another classloader. This lead to very complex code; PowerMock went into that direction, and the code is difficult to maintain.
Also don't mock types you don't own, see the 4-5 first results on google. Why would you need to mock Spring types in a unit test. You should either create some indirection to avoid Spring adherence or write integration tests; the latter seems more appropriate as it is related to JMS.
Upvotes: 1
Reputation: 42834
You still need to set the mock on the object you are initializing.
I see that you are testing IncomingFeedControllerImpl
, and it is likely that your mocked object is a member of an instance of this class. Because you are not setting your mocked DefaultMessageListenerContainer
on your IncomingFeedControllerImpl
explicitly the AbstractJmsListeningContainer
(which is probably @Autowired
) is still hanging around and not being mocked.
You will need to either inject it by using a setter method or a constructor. (you can also @Autowired
it)
Upvotes: 1