Showing posts with label JUnit. Show all posts
Showing posts with label JUnit. Show all posts

Thursday, August 13, 2015

MXJUnit: Testing Crons

MXJUnit provides the ability to unit test crons.  This means that, within a JUnit test case, it is possible to execute a cron and test what it does.  MXJunit goes one step further and allows you to execute the cron with a subclass of what's defined in Maximo.  This allows a change to change the behaviour of the cron for the test.  Normally, I use this to provide a different SQL where clause so the test operates on a smaller and/or known subset of the data.

Most MXJUnit tests will extend from MaximoTestHarness.  A test that has to execute a cron task must extend from CronTaskTestHarness.   This provides methods to execute a cron, but also performs some additional work behind the scenes that isn't normally required.

This example executes the KPIHISTORY cron which goes through the KPIHISTORY table and deletes records more than a year old.  These examples should be executable in any Maximo environment.


 public class CronTaskDemo1Test extends CronTaskTestHarness {  
      /**  
       * Test that KPIHISTORY cron will delete old KPIHISOTRY records.  
       *   
       * @throws RemoteException if an RMI problem occurs.  
       * @throws MXException if a Maximo problem occurs.  
       */  
      @Test  
      public void testKPIHistory() throws RemoteException, MXException {  
           // create a new KPI History record for testing.  
           MboRemote kpiHistory = createKpiHistory();  

           // load it and verify that it exists and is properly populated  
           kpiHistory = refresh(kpiHistory);  
           assertNotNull(kpiHistory);  
           assertEquals(pastDate(), kpiHistory.getDate("RECORDEDON"));  

           // run the cron  
           runCron("KPIHISTORY", "KPIHISTORY1YEAR");  

           // attempt to reload the KPIHISTORY and note that it is gone.   
           kpiHistory = refresh(kpiHistory);  
           assertNull(kpiHistory);  
      }

     // ... snip ...  
 }  

The full source is available in CronTaskDemo1Test.java

The runCron() method executes the cron.  It must refer to an existing definition in the Maximo cron task tables (CRONTASKDEF, CRONTASKINSTANCE).  It will execute the cron with the parameters defined for the named cron task instance.  The cron does not need to be active in Maximo for the cron to execute it.

That's all there is to running a cron in an MXJUnit test.  After the test, make sure to test that the cron performed the intended work.


 public class CronTaskDemo2Test extends CronTaskTestHarness {  
   
      public static class DemoCron extends KPIHistoryCleanupCronTask {  
           @Override  
           public void cronAction() {  
                throw new RuntimeException("Stopped!");  
           }            
      }  
        
      /**  
       * Test that KPIHISTORY cron will delete old KPIHISOTRY records.  
       *   
       * @throws RemoteException if an RMI problem occurs.  
       * @throws MXException if a Maximo problem occurs.  
       */  
      @Test  
      public void testKPIHistory() throws RemoteException, MXException {  
           try {  
                // run the cron  
                runCron(DemoCron.class, "KPIHISTORY", "KPIHISTORY1YEAR");  
                fail("Exception expected");  
           } catch (RuntimeException e) {  
                assertEquals("Stopped!", e.getMessage());  
           }       
      }            
 }  

The full source is available in CronTaskDemo2Test.java

Many crons I've written involve data transfers or data updates.  By writing the cron with a method to retrieve or generate an SQL where clause to identify the data on which to operate, it is possible to use MXJUnit to substitute a different where clause for testing.

This example replaces the cronAction() of the KPIHISTORY cron to throw an error.  A new class, DemoCron is created to extend from KPIHistoryCleanupCronTask.  The cronAction() method is overridden to perform a different action.  The cron is executed using the alternative runCron() that accepts a Class object.  It is that Class that will be instantiated instead of the one defined in the CRONTASKDEF table.


      public static class DemoCron extends PMWoGenCronTask {  
           @Override  
           public void cronAction() {  
                throw new RuntimeException("Stopped!");  
           }            
      } 

The full source is available in CronTaskDemo3Test.java

As a safety precaution,  the CronTaskTestHarness makes sure that the class passed in the first parameter is a subclass of the class defined in CRONTASKDEF.

If the DemoCron is changed to extend from PMWoGenCronTask, the test will fail with a message that "Test class must be a subclass of com.ibm.tivoli.maximo.report.kpi.KPIHistoryCleanupCronTask."  This will catch situations where the cron class has changed in the database or the developer makes a mistake in the class definition.

Tuesday, June 23, 2015

MXJUnit: Introduction

MXJUnit is a unit testing framework for Maximo by Interloc Solutions built on top of JUnit.  It permits testing of classes that normally run in the MXServer --- not UI classes like DataBeans --- within Eclipse itself.  This includes MBOs and field validators, but also crons and the MIF.


MXJUnit provides the following features:
  • capture e-mails from being sent during tests, but also permits writing tests to confirm that an e-mail has been sent and verify its contents.
  • capture logger output.
  • generate work orders.
  • run crons from a test, optionally using a subclass specific to the test.  This allows changing behaviour for the test, usually to have the cron work on a known subset of data.
  • capture output from PublishChannels.
  • send data to an Enterprise Service to test UserExits.
  • send data to an Object Structure to test processing classes.
  • replace Maximo property (MAXPROP) values, system properties and MAXVARS temporarily for the duration of a test.
  • replace an MBO class for the duration of a test.
  • automatically delete MBOs created during the test after the test has completed.

Writing test cases using MXJUnit should follow normal best practices for writing regular JUnit tests.


MXJUnit tests should extend from MaximoTestHarness or one of its subclasses.  Special subclasses exist for testing Crons and for testing MIF.  MaximoTestHarness provides methods for interacting with Maximo.  Some of the more useful methods are:
  • generateWorkOrder:  generates work orders given a PM object and lead time information.
  • getLogCapture: get the logger output generated during the test.
  • getMboSet: get an MboSet, optionally specifying a user and app to enable application security.
  • refresh: given an MBO, reload a fresh copy from the database.
  • refreshUserSecurity: after making ApplicationAuth changes, reloads security settings for a given user.
  • spyOnMXServer: return an MXServer instance that has been mocked using Mockito.

Here is a sample unit test that retrieves a work order by wonum and verifies that the record returned was the record expected.

 package com.interlocsolutions.demo;   
 import java.rmi.RemoteException;   
 import static org.junit.Assert.*;   
 import org.junit.Test;   
 import psdi.mbo.MboRemote;   
 import psdi.mbo.MboSetRemote;   
 import psdi.util.MXException;   
 import com.interlocsolutions.maximo.junit.MaximoTestHarness;   
   
 public class MXDemoTest extends MaximoTestHarness {     
   @Test     
   public void testWorkOrder() throws MXException, RemoteException {       
    // Setup       
    MboSetRemote msr = getMboSet("WORKORDER");       
    msr.setWhere("wonum = '1022'");       
    MboRemote mr = msr.getMbo(0);       
   
    // Execute       
    String wonum = mr.getString("WONUM");           
   
    // Verify       
    assertEquals("1022", wonum);     
   }   
 }  

In the Setup phase, a WORKORDER MboSet is created and the work order with wonum='1022' is retrieved.

In the Execute phase, the WONUM field is retrieved.

In the Verify phase, the WONUM is confirmed to be 1022.  Every test should assert something or the test doesn't test anything.


Here is a sample walk through creating and running an MXJUnit test.