This post originated from an RSS feed registered with Java Buzz
by Weiqi Gao.
Original Post: Unit Tests Are Not Enough
Feed Title: Weiqi Gao's Weblog
Feed URL: http://www.weiqigao.com/blog/rss.xml
Feed Description: Sharing My Experience...
In a post yesterday, Eric gave a concrete example of the drawback of lack of static typing in Ruby:
The second bug took me far longer to understand and was more insidious. In Ruby you don’t even have to say “return” at the end of a method. Instead, the result of whatever expression happened last is automatically returned. Invisible magic is the quintessential example of a feature that saves the original coder a few extra keystrokes but makes every downstream programmer pay the continuous tax for years to come.
I made the problem worse by adding a simple “if” statement to the end of the method, thinking I could explicitly change a bogus error message to nil. But now the method didn’t even return a string any more…I inadvertently changed the return type from a string to a boolean.
To which I commented without too much thinking:
Had you made a similar return-type-altering change in Java, the compiler would have caught it as an error. In that regard, the static typing works just like unit tests—it prevents you from breaking your code.
This post became the topic of discussion at OCI North this morning. Brian mentioned that he has been bitten by the same problem in a Ruby script he wrote recently. He also pointed out that no amount of unit tests will guarantee that a function will return data of a specific type.
Suppose you want to write a function that takes a string and return a boolean in a dynamically typed language, and you want the function to return true for inputs like "yes", "true" and for inputs like "no", "false". So you write your unit tests stating these requirements. And you may choose to implement this function like this (in pseudo-code):
def foo(input) {
if input is "yes" return true
if input is "true" return true
if input is "no" return false
if input is "false" return false
}
However, you cannot write a unit test that will make sure this function always returns a boolean. You can write a specific test that prevents the following code from spoiling the function:
if input is "weiqi" return "gao"
But that test cannot prevent the next spoiler, and the next, and the next, ...
In this sense, a statically declared return type of boolean for the function can be thought of as the equivalent of an infinitely many unit tests.