Thursday, November 02, 2006

Boundary Testing -- CASE: Get Median function

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 median
ASORT(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

No comments: