Tuesday, 20 April 2010
Scala / TestNG gotcha with result type inference
« There's been an explosion in the ASCII factory. | Main | The importance of a fluent interface for building testdata »I recently ran into a Scala / TestNG gotcha that proves how much of a Scala noob I still am (but also the kind of problems you can have with the type inference features).
Scala has two nice features that can make your method definitions very succinct, but as is the case with many of such features, they can cause some problems of their own if you're not careful.
The two features are:
- result type inference
- optional return statement
The result type inference allows you not to specify the result type of a method, allowing the scala compiler to infer it:
// the result type is explicitly defined
def getFooWithResultTypeSpecified() : String = {
return "123"
}
//the result type will be inferred
def getFoo() = {
return "123"
}
val foo = getFoo()
// foo will be inferred to be a String as getFoo was inferred to returning String.
assertEquals(foo,"123")
The optional return statement is exactly that: it allows you to leave out the actual return statement. The compiler will use the result of the last expression as the result type.
def getFoo() = {
"123"
}
val foo = getFoo()
assertEquals(foo,"123")
The gotcha I had was in the combination of these features. TestNG considers test methods to be those methods that are both public and void returning. So let's say I start out with he following method:
def testFoo() = {
val foo = new Foo()
// if doSomeVoidMethod doesn't fail, it's good
foo.doSomeVoidMethod()
}
The result type of testFoo is void, so the method will be executed by TestNG.
Than you change foo.doSomeVoidMethod to foo.doSomeStringReturningMethod.
Now testFoo is inferred to return a String, and the method is no longer executed by TestNG. This was exactly the problem I had, and I spent quite some time figuring out why some of my test methods were no longer being run.
The correct protection against this is always ensuring that your test methods are explicitly declared to return Unit (void) like this:
def testFoo() : Unit = {
val foo = new Foo()
// if doSomeStringReturningMethod doesn't fail, it's good
foo.doSomeStringReturningMethod()
}
The correct protection against this is using procedures instead of functions, like this:
def testFoo() {
val foo = new Foo()
// if doSomeStringReturningMethod doesn't fail, it's good
foo.doSomeStringReturningMethod()
}
Note the missing = that makes all the difference. Thanks to Joey for making that clear.
Posted by at 7:55 AM in Java
[Trackback URL for this entry]
@Joey: yes, you're right. I should have known that since it's in the first pages of the "Programming in Scala" book.
Thanks for the pointer!

Couldn't you also define your tests as procedures instead of functions? IOW,
def testFoo() {}
instead of
def testFoo() = {}
Leaving out the equals defines a procedure which "returns" Unit, without having to be explicit about it.