Forum Discussion

tgeliot's avatar
tgeliot
Contributor
8 years ago

Groovy untyped variables: Just Say No

I got thoroughly bitten in the ass yesterday by using untyped variables in a Groovy script in ReadyAPI.  Consider, what’s wrong with the following code?

 

def iterationCount = 0;

def timeoutMinutes = context.expand( '${#TestCase#SW update timeout minutes}' )

log.info "timeoutMinutes : ${timeoutMinutes}"

 

while (complete == 0){

     log.info("iterationCount : $iterationCount, timeoutMinutes : $timeoutMinutes")

     if (iterationCount >= timeoutMinutes) {

           log.error("Update software timed out after $timeoutMinutes minutes")

           testRunner.fail ("Updating software took too long (> $timeoutMinutes minutes)");

           complete = 1

     } else {

           . . .

     }

     iterationCount++

}

 

 

Hint:  there’s nothing wrong with the call to context.expand or its parameter.

The first call to log.info logs

                timeoutMinutes : 1

The second call logs

                iterationCount : 0, timeoutMinutes : 1

                iterationCount : 1, timeoutMinutes : 1

                iterationCount : 2, timeoutMinutes : 1

                iterationCount : 3, timeoutMinutes : 1

                iterationCount : 4, timeoutMinutes : 1

                iterationCount : 5, timeoutMinutes : 1

etc.

 

WTF?

 

The answer is that iterationCount is implicitly made an integer, and because context.expand returns String, timeoutMinutes is implicitly made a String.

Then in the expression (iterationCount >= timeoutMinutes), timeoutMinutes is converted to an integer, but instead of doing the sane thing like Perl would, and converting the string “1” into the integer 1, Groovy converts “1” into the ASCII value of the character 1, which is 49.  So the loop can iterate 49 times before timing out.

 

The solution to this madness is to never use “def” to declare a variable.  Declare them as what they are:  “int” or “String” or “Boolean” or whatever.  Once I did that, Groovy threw an error at assigning the (String) result of context.expand into the int variable timeoutMinutes, and I knew that I needed to add a call to Integer.parseInt to convert the String result of context.expand into an integer value.

 

This only consumed half a day.  Don’t let it happen to you.

2 Replies

  • nmrao's avatar
    nmrao
    Champion Level 3

    SoapUI properties are stored as String only. It is users responsibility to read it as required type.

    In this case, you can simple use

    def timeoutMinutes = context.expand( '${#TestCase#SW update timeout minutes}' ) as Integer
    • tgeliot's avatar
      tgeliot
      Contributor

      Indeed using "as Integer" is a tidier solution to converting the string to an integer, rather than calling Integer.parseInt or anything like that, thanks.