Skip to main content

Testing Toolbelt: Groovy

Groovy, the apparent golden child of Java, is a mostly dynamic-typed JVM language which seeks to add functionality to the core Java language such that writing and testing code is much easier and requires less boiler-plate code. Some of the features provided by Groovy that I have found useful for testing are:

GroovyBean Properties and Constructors

Groovy attempts to simplify the usage of bean-type object by providing some shortcuts for accesses and assignments. By default, every class accessed through Groovy code is decorated with a default constructor which takes a map (property->value), and a getter and setter for each field defined in the class (for which there is not already a getter/setter). The constructor is a simple way to provide values to properties of the new instance.

properties-sample.groovy

class Customer {
  Integer id
  String name
  Date dob

  static void main(args) {
    /* note: the next statement is functionally equivalent to:
     * in Groovy:
     * def customer = new Customer()
     * customer.id = 1
     * customer.name = "Gromit"
     * customer.dob = new Date()
     * in Java (provided all of the set<name> methods were defined):
     * Customer customer = new Customer();
     * customer.setId(1);
     * customer.setName("Gromit");
     * customer.setDob(new Date());
     */
    def customer = new Customer(id:1, name:"Gromit", dob:new Date())
    // note: calls the getName() method defined below
    println("Hello ${customer.name}")
    // note: references the id field directly
    println("Your id is ${customer.id}")
    // ...not really, the Groovy compiler adds in a
    // default getter for the field.
  }

  public String getName() {
    println "in getter"
    return name
  }
}
Token Replacement

In Groovy, the String class is enhanced to allow for the use of tokens within a String literal. These tokens are of the form ${<property>} and will use the properties values available at the time of creation. Refer back to the above example to see some at work.

Collection Literals

Easily create a list by using the [] syntax, a range by using the <start>..<end> syntax, and a map by using the [<key>:<value>] syntax.

collection-sample.groovy

  def emptyList = []
  def list = [1, 2, 3, 4, 5, 6, 7, 8]
  def range = 1..8
  assert list == range
  def emptyMap = [:]
  def map = [1:2, 3:4, 5:6]
Closures

Closures are simply blocks of code surrounded by curly braces, which can be treated as reference types. Closures may declare arguments and may return values. Closures may be stored in variables.

closure-sample.groovy

  // times is actually a method which takes a number
  // (the target of the call) and a closure
  // print 0 to 10 using a closure
  10.times {int var -> println var} // explicit closure variable and type
  10.times {var -> println var} // explicit closure variable
  10.times {println it} // no variable declaration
Iteration (each)

Iteration of arrays, ranges, lists, and maps has been simplified by the addition of the each method.

each-sample.groovy

  // each is actually a method which takes an iterable
  // (the target of the call) and a closure
  // print 0 to 10 using a closure and a range (also an iterable)
  (0..10).each {int var -> println var} // explicit closure variable and type
  (0..10).each {var -> println var} // explicit closure variable
  (0..10).each {println it} // no variable declaration
  // print a map
  [1:10, 2:20, 3:30].each {key, value -> println "${key}-${value}"}
Coercion (Map and Closure)

Any map or closure may be interpreted as an instance of a specific type simply by using as <type>. This allows you to easily create stand-in objects, provided they have a public default (no-arg) constructor.

coercion-sample.groovy

  def map1 = {println "hi"} as Map
  map1.get("george") // prints hi
  map1["george"] // different way of doing the same thing in Groovy
  def map2 = [get: {key -> return "value"}] as HashMap
  map2["key"] = "hello"
  assert map2["key"] == "value"
GDK

Through the default meta-class implementations used by Groovy, many useful methods (many taking advantage of closures) have been added to the standard Java classes. There's too much to list here, but know that many of the above examples use GDK methods.

GroovyTestCase

The GroovyTestCase class contains a large portion of the testing functionality used by basic Groovy. Since the Groovy team have pledged backwards-compatibility with JDK 1.4, they have also stuck with the non-annotation-based version of JUnit(3.8.*) as a basis for their testing helper class. This is not as bad as it sounds, as many of the language features of Groovy make up for the missing capabilities of JUnit 4.4. Every test class defined using groovy must be a sub-class of GroovyTestCase. Each test method must be public, return void, and be named test<something>.

assert*

all of the assert* methods provided by JUnit are also present in the GroovyTestCase class.

shouldFail(<Throwable>)

similar to the Test annotation when used with the expected argument, the shouldFail method takes a Throwable class type and a closure, executes the closure, and fails if an instance of Throwable is not thrown within the closure.

shouldFail-sample.groovy

  shouldFail(ArrayIndexOutOfBoundsException) {
    println (0..4)[10];
  }
StubFor and MockFor

The StubFor/MockFor classes can be used within Groovy to create stand-in objects when coercion won't work or when you need to over-ride the behavior of a constructor.

stubfor-sample.groovy

  //can't use Map coercion, File does not have default constructor.
  def mockFileType = new MockFor(File)
  mockFileType.demand.exists { return true }
  mockFileType.use {
    def f = new File('DOES_NOT_EXIST.TXT')
    assertThat(f.exists, is(true))
  }

Comments

Popular posts from this blog

Using MonoDevelop to Create an ASP.NET Web Service

NOTE: instructions below are for MonoDevelop 2.6 Beta 2 - built on 2011-04-06 03:37:58+0000Getting StartedCreate a new ASP.NET Web Application in MonoDevelop: From the menu, select: File → New → Solution…Expand C#.Select ASP.NET → Web Application.Enter a name for the ASP.NET project that will be created in the solution in Name:.Change the root location for the solution in Location:, if desired.Change the name of the root solution in Solution Name:, if desired.The Results – IWhat you have after executing the new ASP.NET Web Application project wizard is a solution containing one ASP.NET Web Application project. In the default project view in MonoDevelop, you'll find the following items: Default.aspx – This is the default web form rendered and presented in the browser when http://<server>:<port>/ is accessed. Default.aspx.cs – This C# file contains the developer-created common code and event handlers which can be used to affect the processing of the form.Default.aspx.des…

Testing Toolbelt: SpringJUnit4ClassRunner

The org.springframework.test.context.junit4.SpringJUnit4ClassRunner class is another implementation of the JUnit TestRunner class which is used to enable various features of Spring for every run of the test class and every test within it. To use the features provided by the SpringJUnit4ClassRunner class, you need to mark the class using the RunWith annotation using SpringJUnit4ClassRunner as its parameter.In addition to the custom test runner, you will want to mark the class with the ContextConfiguration annotation. The ContextConfiguration annotation is used to mark classes which will automatically read a Spring configuration file and use it to create an ApplicationContext. By default, this file located at <package path>/<test class name>-context.xml. Use the locations argument to over-ride.The ApplicationContext used by the Spring-integrated test will only be loaded once for the whole test class. This behavior can be over-ridden by annotating a test method with the Dirti…