I have a Spring boot application which receives messages from MQ and create a file and store all the data in that file.
I am trying to test this application but I encountered "MQRFH2 has an invalid value" error when I am using a mock bean for this class.
The code for the Main Application is :
@SpringBootApplication
@EnableJms
@EnableScheduling
public class FileMQApplication {
void receiveMessage() {
try {
MQQueueManager queueManager = getMQQueueManager(queueManagerName);
MQQueue queue = queueManager.accessQueue(queueName, MQConstants.MQOO_INPUT_SHARED);
while (true) {
try {
MQGetMessageOptions gmo = getMQMessageOptions();
gmo.options = setMessageOptions();
System.out.println("beforeee receive msg creationnn ....");
MQMessage receiveMsg = getNewMessage();
System.out.println("beforeee q getttttttt ....");
queue.get(receiveMsg, gmo);
System.out.println("afterr q getttttttt ....");
processMessage(receiveMsg);
System.out.println("afterr proesssss ....");
} catch (MQException mqe) {// exception handling code}
}
queue.close(); queueManager.close(); queueManager.disconnect();
} catch (Exception ex) {// exception handling code}
}
// methods created to help mocking in Unit Testing
protected MQMessage getNewMessage() {
return new MQMessage();
}
protected MQGetMessageOptions getMQMessageOptions() {
return new MQGetMessageOptions();
}
protected MQQueueManager getMQQueueManager(String QMName) throws MQException {
return new MQQueueManager(QMName);
}
protected MQRFH2 getMQRFH2(MQMessage msg) throws MQDataException, IOException {
return new MQRFH2(msg);
}
protected int setMessageOptions() {
return CMQC.MQGMO_PROPERTIES_FORCE_MQRFH2 + CMQC.MQGMO_FAIL_IF_QUIESCING + CMQC.MQGMO_NO_WAIT;
}
private void processMessage(MQMessage message) {
try {
byte[] a = message.messageId;
String messageId = toHex(a);
message.seek(0);
if (CMQC.MQFMT_RF_HEADER_2.equals(message.format)) {
MQRFH2 rfh2 = getMQRFH2(message); // error appears here
int strucLen = rfh2.getStrucLength();
int encoding = rfh2.getEncoding();
int CCSID = rfh2.getCodedCharSetId();
String format = rfh2.getFormat();
int flags = rfh2.getFlags();
int nameValueCCSID = rfh2.getNameValueCCSID();
String[] folderStrings = rfh2.getFolderStrings();
byte[] b = new byte[message.getDataLength()];
message.readFully(b);
}
} catch (Exception e) { e.printStackTrace(); }
}
}
The code for TestClass is :
I am mocking the MQ connectivity related classes like MQQueueManager, MQQueue, MQGetMessageOptions, MQMessage, MQRFH2. I have made the FileMQApplication bean as Spy because I want to mock the methods: getMQRFH2, getNewMessage, getMQMessageOptions, getMQQueueManager so that the rest code is tested with test data.
QQ. Is there some specific way to Mock RFH2 that I am not aware of or is there some mistake while creating Mock bean for MQRFH2 ?
@TestMethodOrder(OrderAnnotation.class)
@SpringBootTest
class FileMQApplicationTests {
@Mock
private MQQueueManager queueManager;
@Mock
private MQQueue queue;
@Mock
private MQGetMessageOptions gmo;
@Mock
private MQMessage msg;
@Mock
private MQRFH2 rfh2;
@Spy
FileMQApplication mainApp;
// values are fetched from properties file under **src/test**
@Value("${mq.queueName}")
private String queueName;
@Value("${mq.host}")
private String host;
@Value("${mq.port}")
private int port;
@Value("${mq.queueManager}")
private String queueManagerName;
@Value("${mq.channel}")
private String channelName;
@Value("${dir.location}")
private String directoryLocation;
@Value("${rejected.dir.location}")
private String rejectedDirLocation;
@Value("${mq.userId:}")
private String uid;
@Value("${mq.password:}")
private String pwd;
@Value("${mq.encryptionSalt:}")
private String encryptionSalt;
@BeforeEach
void init() {
// setting the property values in main app from test properties
ReflectionTestUtils.setField(mainApp, "queueName", queueName);
ReflectionTestUtils.setField(mainApp, "host", host);
ReflectionTestUtils.setField(mainApp, "port", port);
ReflectionTestUtils.setField(mainApp, "queueManagerName", queueManagerName);
ReflectionTestUtils.setField(mainApp, "channelName", channelName);
ReflectionTestUtils.setField(mainApp, "directoryLocation", directoryLocation);
ReflectionTestUtils.setField(mainApp, "rejectedDirLocation", rejectedDirLocation);
ReflectionTestUtils.setField(mainApp, "uid", uid);
ReflectionTestUtils.setField(mainApp, "pwd", pwd);
ReflectionTestUtils.setField(mainApp, "encryptionSalt", encryptionSalt);
try {
when(queueManager.accessQueue(queueName, MQConstants.MQOO_INPUT_SHARED)).thenReturn(queue);
doNothing().when(queue).get(msg, gmo);
doNothing().when(msg).seek(0);
} catch (MQException e) { e.printStackTrace(); } catch (EOFException e) { e.printStackTrace(); }
return;
}
@Test
@DisplayName("Test 1")
@Order(1)
public void processMessageTest1() throws IOException {
File file1 = new File (directoryLocation + "file.txt");
String inputFileName = "file.txt";
String inputFileDir = "./src/test/resources/";
String fullPathFile = inputFileDir + inputFileName;
String requestMessageRef = "file";
String testContent = readFile(fullPathFile, "TestFile");
String testHeader = "<FileName>" + inputFileName + "</FileName>";
String testMessage = testHeader + testContent;
String[] folderStrings = {"some string"};
try {
ReflectionTestUtils.setField(msg, "messageId", requestMessageRef.getBytes());
ReflectionTestUtils.setField(msg, "format", CMQC.MQFMT_RF_HEADER_2);
when(rfh2.getStrucLength()).thenReturn(testMessage.getBytes().length);
when(rfh2.getEncoding()).thenReturn(12000);
when(rfh2.getCodedCharSetId()).thenReturn(CMQC.MQCCSI_DEFAULT);
when(rfh2.getFormat()).thenReturn(CMQC.MQFMT_RF_HEADER_2);
when(rfh2.getFlags()).thenReturn(1);
when(rfh2.getNameValueCCSID()).thenReturn(1);
when(rfh2.getFolderStrings()).thenReturn(folderStrings);
when(msg.getDataLength()).thenReturn(testMessage.getBytes().length);
//when(msg.messageId).thenReturn(requestMessageRef.getBytes());
when(mainApp.getNewMessage()).thenReturn(msg);
when(mainApp.getMQMessageOptions()).thenReturn(gmo);
when(mainApp.setMessageOptions()).thenReturn(CMQC.MQGMO_PROPERTIES_FORCE_MQRFH2 + CMQC.MQGMO_FAIL_IF_QUIESCING + CMQC.MQGMO_NO_WAIT);
when(mainApp.getMQQueueManager(queueManagerName)).thenReturn(queueManager);
when(mainApp.getMQRFH2(msg)).thenReturn(rfh2); // error appears when this executes
when(mainApp.toHex(requestMessageRef.getBytes())).thenReturn("message ID");
mainApp.receiveMessage();
// assert statement for test case
} catch (Exception ex) { ex.printStackTrace(); }
}
}
The error stack trace is :
I have added 2 comments in the stack trace to help identify which line of code gives the error in java file.
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.4.5)
2021-06-02 08:36:42.534 INFO 13581 --- [ main] c.a.u.e.e.filemqApplicationTests : Starting filemqApplicationTests using Java 15.0.2 on upp1 with PID 13581 (started by uppdev in /home/uppdev/eclipse-workspace/filemq/trunk)
2021-06-02 08:36:42.539 DEBUG 13581 --- [ main] c.a.u.e.e.filemqApplicationTests : Running with Spring Boot v2.4.5, Spring v5.3.6
2021-06-02 08:36:42.545 INFO 13581 --- [ main] c.a.u.e.e.filemqApplicationTests : No active profile set, falling back to default profiles: default
2021-06-02 08:36:43.315 INFO 13581 --- [ main] c.a.u.e.e.filemqApplication : Host [127.0.0.1]
2021-06-02 08:36:43.316 INFO 13581 --- [ main] c.a.u.e.e.filemqApplication : Channel [SYSTEM.ADMIN.SVRCONN]
2021-06-02 08:36:43.317 INFO 13581 --- [ main] c.a.u.e.e.filemqApplication : Port [1414]
2021-06-02 08:36:43.699 INFO 13581 --- [ main] o.s.s.c.ThreadPoolTaskScheduler : Initializing ExecutorService 'taskScheduler'
2021-06-02 08:36:43.756 INFO 13581 --- [ main] c.a.u.e.e.filemqApplicationTests : Started filemqApplicationTests in 1.815 seconds (JVM running for 6.399)
2021-06-02 08:36:44.005 INFO 13581 --- [ scheduling-1] c.a.u.e.e.filemqApplication : QueueManagerName [IPMT_QM]
com.ibm.mq.headers.MQDataException: MQJE001: Completion Code '1', Reason '2142'.
at com.ibm.mq.headers.internal.Header.validate(Header.java:735)
at com.ibm.mq.headers.internal.Header.read(Header.java:1057)
at com.ibm.mq.headers.MQRFH2.read(MQRFH2.java:229)
at com.ibm.mq.headers.internal.Header.read(Header.java:1024)
at com.ibm.mq.headers.internal.Header.read(Header.java:985)
at com.ibm.mq.headers.MQRFH2.<init>(MQRFH2.java:128)
at com..utilities..filemq.filemqApplication.getMQRFH2(filemqApplication.java:230) // getMQRFH2(MQMessage msg) <- this line
at com..utilities..filemq.filemqApplicationTests.sendPosDRSTest(filemqApplicationTests.java:176) // when(mainApp.getMQRFH2(msg)).thenReturn(rfh2); <- this line
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:564)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:220)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:188)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:202)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:181)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:150)
at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:124)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:384)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:345)
at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:126)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:418)
Caused by: com.ibm.mq.headers.internal.validator.MQHeaderValidationException: MQHDR0011:Header 'MQRFH2' has an invalid value '
t com.ibm.mq.headers.internal.MQCharField$1.validate(MQCharField.java:181)
at com.ibm.mq.headers.internal.validator.ChainedValidator.validate(ChainedValidator.java:73)
at com.ibm.mq.headers.internal.validator.ChainedValidator.validate(ChainedValidator.java:73)
at com.ibm.mq.headers.internal.HeaderType.validate(HeaderType.java:511)
at com.ibm.mq.headers.internal.Header.validate(Header.java:729)
at com.ibm.mq.headers.internal.Header.read(Header.java:1057)
at com.ibm.mq.headers.MQRFH2.read(MQRFH2.java:229)
at com.ibm.mq.headers.internal.Header.read(Header.java:1024)
at com.ibm.mq.headers.internal.Header.read(Header.java:985)
at com.ibm.mq.headers.MQRFH2.<init>(MQRFH2.java:128)
at com.utilities.filemq.filemqApplication.getMQRFH2(filemqApplication.java:230)
at com.utilities.filemq.filemqApplication$MockitoMock$1966099648.getMQRFH2$accessor$sDzBJ9IF(Unknown Source)
at com.utilities.filemq.filemqApplication$MockitoMock$1966099648$auxiliary$B5FfFFK0.call(Unknown Source)
at org.mockito.internal.invocation.RealMethod$FromCallable$1.call(RealMethod.java:40)
at org.mockito.internal.invocation.RealMethod$FromBehavior.invoke(RealMethod.java:62)
at org.mockito.internal.invocation.InterceptedInvocation.callRealMethod(InterceptedInvocation.java:141)
at org.mockito.internal.stubbing.answers.CallsRealMethods.answer(CallsRealMethods.java:44)
at org.mockito.Answers.answer(Answers.java:98)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:106)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:33)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptSuperCallable(MockMethodInterceptor.java:141)
at com.utilities.filemq.filemqApplication$MockitoMock$1966099648.getMQRFH2(Unknown Source)
... 66 more
2021-06-02 08:36:44.048 ERROR 13581 --- [ main] c.a.u.e.e.filemqApplication : com.ibm.mq.headers.MQDataException: MQJE001: Completion Code '1', Reason '2142'.
2021-06-02 08:36:44.090 INFO 13581 --- [ scheduling-1] c.a.u.e.e.filemqApplication : QueueName [tch.in.ib.q]
beforeee receive msg creationnn ....
beforeee q getttttttt ....
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.319 s - in com.utilities.filemq.filemqApplicationTests
2021-06-02 08:36:44.142 INFO 13581 --- [ scheduling-1] c.a.u.e.e.filemqApplication : No messages waiting in the queue...
2021-06-02 08:36:44.240 INFO 13581 --- [extShutdownHook] o.s.s.c.ThreadPoolTaskScheduler : Shutting down ExecutorService 'taskScheduler'
Please help me to identify what's wrong and how to counter it.
As is Evident from the error trace there's the exception : MQDataException
This exception occurs when MQRFH2 has invalid data. But the only problem is I have Mocked MQRFH2 bean so technically it should not give this error.
Thanks
I found the working solution to my problem.
I changed the statement:
when(mainApp.getMQRFH2(msg)).thenReturn(rfh2);
to the following:
Mockito.doReturn(rfh2).when(mainApp).getMQRFH2(msg);
Now the error "MQRFH2 has invalid value" does not appear.
These 2 statements should basically both work but in this case of unit test, only one works.
I have found out that there are some cases in which when().thenReturn() does not work / gives compile time error and for the same case doReturn().when() works completely fine.
The answer I found most helpful is available here. Adrian Shum's answer is very helpful along with Dawood ibn kareem.
There is also one more link to a detailed answer by akcasoy here. He explains with an example how doReturn and when are different with a satisfactory explanation.