szymon
szymon

Reputation: 161

How to mock DefaultMessageListenerContainer

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

Answers (4)

user6536621
user6536621

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

jhericks
jhericks

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

bric3
bric3

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

nicholas.hauschild
nicholas.hauschild

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

Related Questions