Search code examples
javaspring-bootibm-mqjunit5spring-jms

Mock MQRFH2 header in JUnit Testing Error [MQRFH2 has invalid value]


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


Solution

  • 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.