Last week I talked about boundary testing parameters with a promise to discuss testing methods this week. I’m going to adjust this promise a little due to the magnitude of the task. I felt it would be beneficial to review a case first.

This week, I present a simple program that I wrote several years ago that calculates the median of a range of values. I have enhanced (famous last words) the program to accept a variety of inputs that will help to demonstrate my boundary testing example next week. It has a driver parameter (tnType) and two additional parameters (teName, teColDel) that hold different data types depending on the driver.

There is no concept of overloading functions in VFP. The technique I am using for the get_median fucntion below simulates this concept. Finding the median of a cursor, array or delimeted string is not particularly hard to do, but developing a bullet-proof solution that handles each case in one shot, is.

The following program works, but has several flaws. Next week I will present an improved get_median function that addresses these flaws. I’ll expose some of them using boundary testing routines.

*-- The following creates a simple test to run get_median
cString = "1,2,3,4,5"
CREATE CURSOR crTemp (col1 c(1), value n(1))
INSERT INTO crTemp VALUES ("1",1)
INSERT INTO crTemp VALUES ("2",2)
INSERT INTO crTemp VALUES ("3",3)
INSERT INTO crTemp VALUES ("4",4)
 
DIMENSION aTestMedian[5,2]
aTestMedian[1,1] = 1
aTestMedian[2,1] = 2
aTestMedian[3,1] = 3
aTestMedian[4,1] = 4
aTestMedian[5,1] = 5
 
? get_median(1,cString,',')
? get_median(2,'crTemp','value')
? get_median(3,'aTestMedian',1)
 
*-- The following is the first version of get_median
FUNCTION get_median
    LPARAMETERS tnType, teName, teColDel
 
    *-- local declerations
    LOCAL lcSelCol AS String
    LOCAL lnItems, lnHalf, lnInc AS Integer
    LOCAL lnMedian AS Number
 
    *-- create the aMedian Array
    DO CASE
    CASE tnType = 1        && use a delimted string
        lnItems= ALINES( aMedian , STRTRAN(teName ,teColDel,CHR(13)) )
        FOR lnInc = 1 TO lnItems
            aMedian[lnInc] = VAL(aMedian[lnInc])
        NEXT
    CASE tnType = 2       && calculate using an alias
        lcSelCol = FIELD(teColDel,teName)
        lcSQL = "SELECT " + lcSelCol + " FROM " + teName + ;
              " ORDER BY " + lcSelCol + " INTO ARRAY aMedian"
        &lcSQL
        lnItems= _TALLY
    CASE tnType = 3        && calculate using an array
        lnItems= ALEN(&teName,1)
        DIMENSION aMedian[lnItems]
        FOR lnInc = 1 TO lnItems
            aMedian[lnInc] = &teName.[lnInc,teColDel]
        NEXT
    ENDCASE
 
   *-- find the medianASORT(aMedian)
    lnHalf = CEILING( lnItems / 2)
    IF MOD(lnHalf,2) = 0
        lnMedian = ( aMedian[lnHalf] + aMedian[lnHalf+1] ) / 2
    ELSE
        lnMedian = aMedian[lnHalf]
    ENDIF
 
    RETURN lnMedian
 
ENDFUNC