What Groovy auto-imports
I had heard of the Spock testing framework before, I knew IntelliJ IDEA (even the Community Edition) has Spock. Or, more precisely, IntelliJ can have Maven download it and add it to the XML file with the dependencies.
So I decided to give Spock a try. I made a Hello World in Java, just to have something simple to test.
package org.example;
public class HelloWorld {
// TODO: Write tests for this
public static void main(String[] args) {
System.out.println("World, hello?");
}
}
That should fail the test. I told IntelliJ to make a test class using the Spock framework. Much to my surprise, it made the test class not in Java but in Groovy, a programming language that I had heard of before but had never programmed in.
After a little fumbling around, I wrote my first Spock test based on an example I found in a Google search. The following is a little cleaned up from my first draft.
My first draft was about what you’d expect from a Java programmer writing Groovy for the first time. My friend Mark helped me make it more idiomatic for Groovy, but I have purposely left three warnings from my first draft in the following version:
package org.example
import java.io.ByteArrayOutputStream // Warning, unused import?
import java.io.OutputStream // Warning, unused import?
import java.io.PrintStream // Warning, unused import?
import spock.lang.Specification
class HelloWorldTest extends Specification {
def "Hello World should say Hello World"() {
given:
def expected = "Hello, world!"
when:
def usualOut = System.out // originally had type declared
def interceptor = new ByteArrayOutputStream()
def tempOut = new PrintStream(interceptor)
System.setOut(tempOut)
HelloWorld.main()
System.setOut(usualOut)
def actual = interceptor.toString().replace("\n", "")
.replace("\r", "")
then:
expected == actual // originally "expected.equals(actual)"
}
}
Notice that even though I didn’t declare main()
as “varargs,” I don’t need to put in an empty array in the call from Groovy like I would in a call from Java.
The three warnings surprised me, because I’m clearly using those three java.io
classes in the test. Or at least I was in the first draft, it looks like I still need the other two imports.
Since Groovy’s “optionally” typed, it’s not necessary to explicitly declare usualOut
as a PrintStream
, but I think I still need that identifier for the constructor call to initialize tempOut
.
On the other hand, I only needed the OutputStream
import because in Java we prefer to use the highest superclass on the left of an equals sign that we can manage. But that still doesn’t explain why the ByteArrayOutputStream
and PrintStream
imports are not used in this test class in Groovy.
I didn’t realize that Groovy would automatically import everything from the java.io
package.
In fact, according to the official Groovy documentation, the Groovy compiler will by default automatically import the following:
import java.lang.*
import java.util.*
import java.io.*
import java.net.*
import groovy.lang.*
import groovy.util.*
import java.math.BigInteger
import java.math.BigDecimal
Aside from java.lang
, groovy.lang
and groovy.util
, these choices seem a little arbitrary to me. Nevertheless, when learning a new programming language, it’s a good idea to pay attention to the warnings your integrated development environment reports, not just the errors.