It's a well documented fact that the earlier a bug is discovered the easier it is to fix. One of the challenges of writing applications to run in CICS is that you have to have a region available for running the applications in. If this is then shared with other developers you can run into issues as only one remote debugger can be attached to a JVM server at any one time.

A potential solution is to write automated unit tests that run inside the development environment, but before we can do that we need to be able to use the JCICS APIs outside of a CICS region.

The easiest way to do this is to using a Mock Object framework which allows us to stub out Objects and control the behaviour of methods on those objects. In the following examples we make use of the Mockito and PowerMock frameworks.

For the example we are going to be using a very simple CICS program which links to another CICS program and returns the result of that link in the COMMAREA. The code is as follows:

import com.ibm.cics.server.*;

public class MockDPL {

   public static void main(CommAreaHolder cah){
      Program prog = new Program();
      prog.setName("EC01");

      try {
         prog.link(cah.getValue());
      } catch (InvalidRequestException | LengthErrorException
            | InvalidSystemIdException | NotAuthorisedException
            | InvalidProgramIdException | RolledBackException
            | TerminalException e) {
         byte[] errorBytes = "Error".getBytes();
         System.arraycopy(errorBytes, 0, cah.getValue(), 0, errorBytes.length);
      }
   }
}

The first thing to test is the success case. To do this we have to replace the Program object that would be created with a Mock object that will stub out the link call. The test code looks like:

@RunWith(PowerMockRunner.class)
@PrepareForTest(MockDPL.class)
public class MockDPLTest {

   @Test
   public void testMain() throws Exception{
      Program p = mock(Program.class);
      whenNew(Program.class).withNoArguments().thenReturn(p);
      CommAreaHolder cah = mock(CommAreaHolder.class);
      when(cah.getValue()).thenReturn("Result".getBytes());

      MockDPL.main(cah);

      assertEquals("Commarea values not equal", "Result", new String(cah.getValue()));
   }
}

The first annotation on the class definition tells JUnit that we want to use the PowerMockRunner class to run the tests rather than the standard JUnit TestRunner class. The second tells the PowerMock framework that we will be inserting mock objects into the MockDPL class.

The test then performs the following steps:

  1. Create a mock object version of the Program class.
  2. Tell PowerMock to return this mock object whenever a call to the no-arguments constructor is made.
  3. Create a mock object version of the CommAreaHolder class.
  4. Configure the mock object so return a specific byte array when the getValue() method is called. This is to simulate the COMMAREA that would be returned by the linked program.
  5. Call the JCICS program, passing in the mocked CommAreaHolder.
  6. Finally test that the value in the CommAreaHolder matches what is expected.

This test covers our expected path when everything works correctly and there are no issues when running the target CICS program. However given that there are a number of Exceptions that can be thrown by the link method, and we have specific processing in the catch block we should test that code path as well.

The test code looks like:

@Test
public void testMainWithException() throws Exception{
   Program p = mock(Program.class);
   whenNew(Program.class).withNoArguments().thenReturn(p);
   CommAreaHolder cah = new CommAreaHolder(new byte[100]);

   doThrow(InvalidProgramIdException.class).when(p).link(any(byte[].class));

   MockDPL.main(cah);

   assertEquals("Error message not set", "Error", new String(cah.getValue()).trim());
}

In this test we perform the same steps to create the Program mock object and inject it into the runtime. However this time when the link method is called with any byte array then an InvalidProgramIdException will be thrown. We can then check that the correct error is returned in the COMMAREA.

Using these techniques, of inserting mock objects into the existing program and then controlling their behaviour to drive the various code paths allows us to prove the correctness of the code (according to the specifications) before we deploy it to the test regions.

3 comments on"Unit testing JCICS applications"

  1. SIMON MILLER October 01, 2018

    this was helpful, thanks. There is a major dearth of jcics examples for java programmers

  2. Tony Clifford October 15, 2018

    This would be far more useful if you were to include the project setup steps. Not everyone is familiar with the Mockito or PowerMock frameworks. A lot of CICS people like myself are from a mainframe background and not familiar with this environment.

    • Andrew Smithson October 16, 2018

      Hi Tony,

      Thank you for the feedback. I will look at getting a sample project shared on GitHub which could be cloned and used, along with a set of setup instructions.

      Andrew

Join The Discussion

Your email address will not be published. Required fields are marked *