Issue
When running my test, the following when/then returns null
in the class being tested:
when(connection.createSession(false, Session.AUTO_ACKNOWLEDGE)).thenReturn(producerSession);
The NullPointerException
is thrown in the constructor.
The full test class looks like this:
@RunWith(MockitoJUnitRunner.class)
public class HeartbeatTest {
@Mock
private static Connection connection;
@Mock
private static ActiveMQConnectionFactory mqConnectionFactory;
@Mock
private static ScheduledExecutorService scheduledExecutorService;
@Mock
private static MessageProducer heartbeatProducer;
@Mock
private static AbstractConfiguration config;
@Mock
private static ConnectionFactory cf;
@Mock
private static Session producerSession;
@InjectMocks
TestableHeartbeatImpl heartbeatImpl;
@Test
public void processHeartbeatSuccessfullyTest() throws JMSException {
when(config.getModuleName()).thenReturn("testModule");
when(config.getModuleQueue()).thenReturn("testqueue");
when(config.getModuleId()).thenReturn("abcd1234");
when(connection.createSession(false, Session.AUTO_ACKNOWLEDGE)).thenReturn(producerSession);
when(producerSession.createProducer(any(Queue.class))).thenReturn(heartbeatProducer);
when(heartbeatImpl.createConnection(cf)).thenReturn(connection);
heartbeatImpl.acknowledge(1000);
verify(scheduledExecutorService.schedule(any(Runnable.class), anyLong(), any()), times(3))
.isDone();
}
static class TestableHeartbeatImpl extends HeartbeatImpl {
public TestableHeartbeatImpl() throws JMSException {
super(config);
}
@Override
protected Runnable createHeartbeatMessageRunnable(){
return super.createHeartbeatMessageRunnable();
}
@Override
protected ActiveMQConnectionFactory getActiveMQConnectionFactory(String artemisConnection){
System.out.println("Calling overridden getActiveMQConnectionFactory");
return mqConnectionFactory;
}
@Override
protected Connection createConnection(ConnectionFactory cf){
return connection;
}
}
}
The class under test looks like this, the mock producerSession
is not injected and so the NullPointerException
is thrown when a method is called on it:
public class HeartbeatImpl implements Heartbeat {
private static final Logger logger = LogManager.getLogger(HeartbeatImpl.class);
private final Session producerSession;
private final MessageProducer heartbeatProducer;
private final long timeoutPeriodMillis;
private final long hearbeatPeriodMillis;
private final AbstractConfiguration conf;
private final String moduleName;
private final Queue privateQueue;
private final ScheduledExecutorService scheduledExecutorService;
private ActiveMQConnectionFactory mqConnectionFactory;
private Runnable heartbeatMessageRunnable;
public HeartbeatImpl(AbstractConfiguration conf) throws JMSException {
this.conf = conf;
this.moduleName = conf.getModuleName();
ConnectionFactory cf = createConnectionFactory(conf);
Connection connection = createConnection(cf);
producerSession = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
privateQueue = new ActiveMQQueue(conf.getModuleQueue() + ".private." + conf.getModuleId());
logger.debug("creating heartbeat producer for {}", conf.getModuleQueue() + ".private." + conf.getModuleId());
heartbeatProducer = producerSession.createProducer(privateQueue);
timeoutPeriodMillis = conf.getHeartbeatTimeout();
hearbeatPeriodMillis = conf.getHeartbeatPeriod();
scheduledExecutorService = Executors.newScheduledThreadPool(1);
logger.trace("Scheduling first heartbeat message");
heartbeatMessageRunnable = createHeartbeatMessageRunnable();
scheduledExecutorService.schedule(heartbeatMessageRunnable, hearbeatPeriodMillis, TimeUnit.MILLISECONDS);
}
@Override
public void acknowledge(long sendTime) {
long receiveTime = System.currentTimeMillis();
logger.trace("Heartbeat received after {}ms", receiveTime - sendTime);
((HeartbeatMessageRunnable) heartbeatMessageRunnable).getScheduledFuture().cancel(true);
heartbeatMessageRunnable = createHeartbeatMessageRunnable();
logger.trace("scheduling heartbeat message");
scheduledExecutorService.schedule(heartbeatMessageRunnable, hearbeatPeriodMillis, TimeUnit.MILLISECONDS);
}
@Override
public void shutdown() {
logger.info("Shutting down heartbeatService");
scheduledExecutorService.shutdownNow();
try {
heartbeatProducer.close();
} catch (JMSException e) {
logger.error("Cannot shutdown heartbeatProducer", e);
}
}
private ConnectionFactory createConnectionFactory(AbstractConfiguration conf) {
String artemisConnection = conf.getArtemisConnection();
ConnectionFactory cf = getActiveMQConnectionFactory(artemisConnection);
logger.debug("Artemis connection correctly established: {}", artemisConnection);
return cf;
}
protected ActiveMQConnectionFactory getActiveMQConnectionFactory(String artemisConnection) {
if (mqConnectionFactory == null) {
return new ActiveMQConnectionFactory(artemisConnection);
} else {
return mqConnectionFactory;
}
}
protected Connection createConnection(ConnectionFactory cf) throws JMSException {
return cf.createConnection(conf.getArtemisUsername(), conf.getArtemisPassword());
}
protected Runnable createHeartbeatMessageRunnable(){
if (heartbeatMessageRunnable == null) {
return new HeartbeatMessageRunnable();
} else {
return heartbeatMessageRunnable;
}
}
// Below are the Runnable classes used by this service
class HeartbeatMessageRunnable implements Runnable {
private ScheduledFuture<?> scheduledFuture;
@Override
public void run() {
try {
TextMessage heartBeatMessage = producerSession.createTextMessage();
heartBeatMessage.setStringProperty(PARAM_MODULE_NAME, moduleName);
heartBeatMessage.setIntProperty(PARAM_SECURITY_LEVEL, 0);
heartBeatMessage.setStringProperty(PARAM_ACTION, CMD_HEARTBEAT_ACK);
heartBeatMessage.setLongProperty(PARAM_TIMESTAMP, System.currentTimeMillis());
heartbeatProducer.send(heartBeatMessage);
scheduledFuture = scheduledExecutorService.schedule(new HeartbeatShutdownRunnable(), timeoutPeriodMillis, TimeUnit.MILLISECONDS);
} catch (JMSException e) {
logger.error("An exception occurred when trying to send a heartbeat message", e);
}
}
private ScheduledFuture<?> getScheduledFuture() {
return scheduledFuture;
}
}
static class HeartbeatShutdownRunnable implements Runnable {
private final Logger logger = LogManager.getLogger(HeartbeatShutdownRunnable.class);
@Override
public void run() {
logger.fatal("Heartbeat failed, shutting down module");
System.exit(1);
}
}
}
What am I doing wrong?
Solution
Using @InjectMocks
seems to be wrong here.
After removing that annotation and instantiating the class in the test itself I can get things working.
This also meant I no longer needed attributes and internal classes to be static.
Answered By - mal
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.