Tuesday, September 8, 2009

Extending FitNesse For Testing RPG

It is the experience of most that in the iron triangle of "Cost, Quality and Time - Choose Any Two" that time and cost are fixed and that quality is variable. There is never enough time to perform unit testing, regression testing and user acceptance testing as thoroughly as we would like but, there is a solution. FitNesse can perform tests at the click of a button.

FitNesse (www.fitnesse.org) is a testing component that allows you and your project team members to specify and run software tests. FitNesse is a wiki. Tests are specified by entering wiki markup on a webpage and tests are run by clicking the 'Test' button on the web page. The advantages of this system quickly become apparent,

1. tests are stored in the webpage where they can be updated and added to as the software being tested changes and grows
2. tests are readily available for all to review, edit and run
3. tests can be run quickly and repeatedly

It is quite simple and easy to start the FitNesse wiki. The wiki runs from a single jar file. Start the wiki by executing the following command in the directory in which the jar is located:
java -jar fitnesse.jar 
The ease with which tests can be specified and run is quite surprising but, there is a catch, a little bit of 'glue' code must be written to connect the backend, in this case, RPG stored procedures, to the frontend, the FitNesse wiki.

The creators of FitNesse envisioned its use will be distributed among many members of the project team. The first step in the use of FitNesse will generally be the entry of a test. The requirements of a project may require the system to be able to retrieve the name of the television show is stored in the System i's database for a particular date and time so the analyst can specify a test in the FitNesse wiki with the following markup:
!path C:\dev\bin\fitnesse\fitnesse.jar 
!path C:\dev\bin\jt400\lib\jt400.jar 
!path C:\dev\src\java\Tivo\bin\ 

!| com.tv.GetScheduledProgramStoredProcFixture |

| scheduled time | scheduled show? |

| 12/01/2009 12:00:00 | Cagney and Lacy | 
The first three lines of the markup above consists of the paths to the Java components the test will need at runtime, the FitNesse jar, the JDBC jar and the code we are about to write:

The fourth line of text in the wiki markup is the qualified name of the Java class we are about to create. This class contains the glue code mentioned before. Although the business analyst may have entered a value here, the developer should be free to change it as necessary to fulfill whatever technical demands and constraints are placed on the Java.

The fifth line is column headings and these translate into field names and method calls in the Java - this is where the FitNesse magic starts to happen. The first column, 'scheduled time', used used by FitNesse to set a field in the Java code. The value the field will be set to is the value in the row below it, 12/0112009 12:00:00. Because FitNesse directly manipulates the value of the field, this field will have to be declared as a public field in the Java class.

FitNesse translates the second column heading, 'scheduled show?', into a method call. The question mark indicates that this method will return a value. The value returned must equal the value on the row below this column heading in order for the test to pass.

At this point, the test will fail if it is run as the necessary components are not in place. The message, as shown here, states "Could not find fixture: com.tv.GetScheduledProgramStoredProcFixture" .

Now the purpose of the wiki markup is clear and the steps needed to take in order for the test to run successfully are evident. The RPG program and its stored procedure wrapper will need to accept a date and time in one parameter and return the name of a television show in the second parameter.
h option(*srcstmt:*nodebugio) 
 * 
 * GETSKEDSHO - return the TV show for time passed in 
 * 
d getskedsho      PR
d showtime                       19A
d showName                       50A

d getskedsho      PI
d showtime                       19A
d showName                       50A 

 /free  
        showName = 'Cagney and Lacy'; 
        *inlr = *on; 
 /end-free 
At this point, the television show name can be hard-coded and returned in the second program parameter. The code to access the database and retrieve the value for the date and time passed to the program can be added to the RPG in the next iteration.

And now to create a SQL wrapper for the RPG program:
CREATE PROCEDURE MYLIB/GETTIVOSHO(IN CHAR(19), OUT CHAR(50))

EXTERNAL NAME MYLIB/GETSKEDSHO 

LANGUAGE RPGLE 

PARAMETER STYLE GENERAL 


Having reviewed the wiki markup and created the RPG component, there is nothing more to do other than create the Java code that connects the two. (Warning: this code does not have all the qualities we would want in production business code or even in running test code. You will quickly find that you don't want to hard-code a user-id and password in your test code but, for the sake of brevity and clarity, the code in this example will lack such refinement.)

package com.tv; 

import java.sql.CallableStatement;
import java.sql.Connection; 
import java.sql.DriverManager; 
import fit.ColumnFixture; 

public class GetScheduledProgramStoredProcFixture extends ColumnFixture { 
   public String scheduledTime; 

   public String scheduledShow() throws Exception { 

      Class.forName("com.ibm.as400.access.AS400JDBCDriver") ; 
      Connection conn = 
         DriverManager.getConnection( "jdbc:as400://AS400DEV;naming=system;prompt=false", "userid" , "password");

      //get scheduled show from database    
      CallableStatement callableStmt = conn.prepareCall("{call MYLIB/GETTIVOSHO(?,?) }"); 

      callableStmt.setString(1, scheduledTime); 

      callableStmt.registerOutParameter( 2, java.sql.Types.CHAR); 

      callableStmt.execute() ; 

      String result = callableStmt.getString(2); 

      try { 
         callableStmt.close() ; 
      } catch(Exception e) {} 
      try { 
         conn.close() ; 
      } catch (Exception e) {} 

      return result.trim(); 
   }
} 

Having compiled the RPG, SQL and the Java, the test should pass when run. All that is necessary is to return to the wiki page and click 'Test' to rerun the test. The result is green which indicates that the test passed.

At this point, we know we have the necessary components in place and working. The RPG code can quickly be modified so that it actually queries the database and returns the value found for the argument passed in the show data and time parameter. And then, of course, rerun the test. I think you'll find that testing in this manner can be quite addictive!