Skip to content

Approach to Mocks

Dave Nicolette edited this page Jan 29, 2021 · 28 revisions

Home -> Development Guide -> Implementation Notes ->

As of January 2021, support for mocks has not been implemented in Cobol Check.

We want to take a slightly different approach than was taken in the proof of concept project, cobol-unit-test. In that project, a mock represented behavior, such as reading a file or calling a subprogram.

In Cobol Check, a mock represents (or will represent) an external resource - a file, a subprogram, etc. - and the user can specify one or more behaviors for that resource.

All references to external resources will be stubbed. This is not left as an option for the user. We explicitly and deliberately want to discourage external dependencies in unit test cases, and we want the tests to be runnable off-platform, outside the usual mainframe execution environment, where many such external dependencies will not be available and possibly cannot be simulated. Mock behavior can be defined for the stubbed resources to support the needs of test cases.

We expect this approach to be easier for users and less error-prone than the previous approach.

Here are some examples of how users might code Mocks in Cobol Check (subject to change as development continues).

Mocking a batch file

Hypothetical scenario: A batch update of taxpayer information for a government agency that processes tax returns. We want to check that the program populates the correct "error code" values (as defined for that application) when the inbound sequential update file is not found, and another case when an input record doesn't have a postal code for the taxpayer's street address.

When Cobol Check reaches the point in the program where it opens the file (somewhere inside paragraph 1100-open-files), instead of actually trying to open the file the generated test program will set the value "35" in the file-status item defined for Taxpayer-Update-File. The token "file-not-found" is syntactic sugar for the Cobol file status code 35, which indicates the file was not found.

When Cobol Check reaches the point in the program where it reads the file (somewhere inside paragraph 1500-validate-in-rec), instead of actually reading the file the generated test program will move predefined test values into various fields in the input record area, including moving spaces to the postal code field, which we think will trigger the desired error-handling behavior.

    TestCase "It handles file-not-found"
        mock Taxpayer-Update-File
          on open file-status is file-not-found 
        end-mock
        perform 1100-open-files 
        expect w-error-code to be "TUFNOTFND" 

    TestCase "It handles missing postal code" 
        mock Taxpayer-Update-File 
          on read 
              move "testID123" to tp-in-taxpayer-id 
              move "55 main st." to tp-in-taxpayer-addr1 
              move "bakersfield, ca" to tp-in-taxpayer-addr2 
              move spaces to tp-in-taxpayer-postcode 
        end-mock 
        perform 1500-validate-in-rec 
        expect ws-error-code to be "TUFNOPOST" 

Mocking a CICS resource

Hypothetical scenario: A CICS application functions as a TCP/IP server. When a message arrives on the input port, CICS populates an item in a CICS Temp Storage Queue. A CICS task (the COBOL program we test in this example) performs a blocking read on the queue, and CICS dispatches the task whenever an item becomes available to read from the queue. The program processes items from the queue in a loop until the queue is once again empty, and the task is suspended.

For each request item, the program looks at the first 4 bytes to determine what sort of request it is, and starts another CICS task to process the request and send the response to the client. We want to write unit test cases to check whether the program initiates the correct CICS task based on particular values it finds in the first 4 bytes of the request.

Maybe the program has CICS commands like these:

    . . .
    EXEC CICS READQ TS
         QUEUE("TCPREQUEST")
         SET(ADDRESS OF REQUEST-DATA)
         LENGTH(256)
         NEXT

    . . .

    EXEC CICS START
         TRANSID(TRANS-TO-START) 
         FROM(REQUEST-DATA)
         LENGTH(LENGTH OF REQUEST-DATA) 
         REQID("TCPWORKER")

    . . .

We might mock the Temp Storage Queue as follows. This assumes the READQ TS command is somewhere inside paragraph GET-NEXT-REQUEST, and we only need to check that the correct value has been set in Data Division item TRANS-TO-START. These test cases don't touch the EXEC CICS START command, so there's no need to mock it here.

    TestCase "It chooses transaction RTB4 to handle message type "X209" 
        Mock QUEUE "TCPREQUEST"  
          on READQ
              move "X209" to REQUEST-DATA(1:4) 
        End-Mock 
        perform GET-NEXT-REQUEST 
        Expect TRANS-TO-START to be "RTB4" 

    TestCase "It chooses transaction RTB5 to handle message type "X211" 
        Mock QUEUE "TCPREQUEST" 
          on READQ
              move "X211" to REQUEST-DATA(1:4) 
        End-Mock 
        perform GET-NEXT-REQUEST 
        Expect TRANS-TO-START to be "RTB5" 

One resource, multiple behaviors

Some programs contain multiple references to an external resource in the same paragraph. Consider the following hypothetical code snippet:

PROCESS-THE-FILE.
    EXEC CICS STARTBR
         FILE(WS-FILENAME) 
         RIDFLD(WS-PRIMARY-KEY)
         KEYLENGTH(LENGTH OF WS-PRIMARY-KEY) 
         NOSUSPEND
         GTEQ
    END-EXEC 
    PERFORM WITH TEST BEFORE 
        VARYING TABLE-IX
             FROM 1 BY 1
             UNTIL TABLE-IX > TABLE-MAX 
        EXEC CICS READNEXT 
             FILE(WS-FILENEXT) 
             RIDFLD(WS-PRIMARY-KEY) 
             KEYLENGTH(LENGTH OF WS-PRIMARY-KEY) 
             INTO(WS-RECORD-AREA) 
             NOSUSPEND
        END-EXEC
        IF EIBRESP = ZERO 
             PERFORM PROCESS-RECORD 
        ELSE 
             PERFORM BAD-RECORD
        END-IF 
    END-PERFORM
    EXEC CICS ENDBR
         FILE(WS-FILENAME) 
         NOSUSPEND
    END-EXEC     
    .

Let's say we want to check the program's behavior in the event the STARTBR and READNEXT commands work, but there is an I/O error on the ENDBR command. We need to mock three behaviors on the file named in WS-FILENAME as well as the paragraphs PROCESS-RECORD and BAD-RECORD.

    TestCase "It handles IOERR gracefully" 
        Move 1 to TABLE-MAX
        Mock FILE WS-FILENAME 
            On STARTBR 
                EIBRESP is ZERO 
            On READNEXT 
                EIBRESP is ZERO 
            On ENDBR 
                EIBRESP is IOERR 
        End-Mock 
        Mock Paragraph PROCESS-RECORD 
        End-Mock 
        Mock Paragraph BAD-RECORD 
        End-Mock 
        Perform PROCESS-THE-FILE 
        Verify BAD-RECORD happened once 
        Expect WS-ERROR-MESSAGE to be "Unexpected error reading file <foo>"
Clone this wiki locally