Sunday, November 29, 2009

Groovy: Switching on Enumerations

Recently, in Groovy, I had a need to switch on a variable that contained an enumerated value and was wondering what I should use in my case clauses. For example given the following:

enum OS { LINUX, MAC, WINDOWS }
Should (or could) I switch on OS.LINUX or "LINUX", or even "${OS.LINUX}"? I wrote a little script to help me decide:

enum OS { LINUX, MAC, WINDOWS }

[OS.LINUX, "LINUX", OS.MAC, "MAC", OS.WINDOWS, "WINDOWS"].each { os ->
switch(os) {
case OS.LINUX:
println "Enum: OS.LINUX"
break
case "${OS.LINUX}":
println "String: LINUX"
break
case OS.MAC:
println "Enum: OS.MAC"
break
case "WINDOWS":
println "String: WINDOWS"
break
default:
println "Unknown OS: '$os'"
}
}

The output from the above:

  LINUX => Enum: OS.LINUX
LINUX => String: LINUX
MAC => Enum: OS.MAC
MAC => Unknown OS: 'MAC'
WINDOWS => String: WINDOWS
WINDOWS => String: WINDOWS

Looking at the results, I'm not sure I really have an answer. Using the "WINDOWS" case as an example, the first two cases could be replaced with "LINUX". But then, am I really switching on an enumerated value or just taking advantage of the fact that enumerations are easily converted to strings?

At the very least, this shows there are options depending on how strict I might want to be with the typing of the variable to be switched on.

Saturday, November 14, 2009

Parameterized Testing with JUnit 4

I have seen several examples in the past of using parameterization with JUnit 4, but always felt like I didn't completely grasp what was going on. So if I don't understand something, I should try it out right?

My example class to test with parameterization will be a nice car with one state-changing method that will add a number of miles to the total traveled, whose value will be stored in a odometer property:

    package prystasj.junit

class Lambo {

int odometer

def drive(miles) {
if (miles > 0) odometer += miles
}

@Override String toString() {
"${getClass().getName()}[odometer=$odometer]"
}
}

To test the odometer, I will "drive" distances of 5, 10, then 20 miles, checking the odometer along the way. At the end, my low-use Lambo should have traveled a total of 35 miles. To do so, I will use the same Lambo instance throughout the tests.

The @BeforeClass annotation will run the annotated static method once per test run, so I will initialize my Lambo, which also must be static, there:

    static Lambo lambo

@BeforeClass static void createLambo() {
lambo = new Lambo()
println "Created Lambo: $lambo"
}

For every run of my test method, I want to receive a number miles to drive and the expected value of the odometer afterwards. Therefore, I need a way to set these values in my test class via a constructor:

    def total
def miles

LamboMileageTest(expectedTotal, milesToDrive) {
this.total = expectedTotal
this.miles = milesToDrive
}

The test parameters come from a static data method that returns a collection of arrays. Each entry in the collection will have its values passed as parameters to my constructor above:

    @Parameters static def data() {
def data = []
def expectedTotal = 0
[5, 10, 20].each { miles ->
expectedTotal += miles
def group = [miles, expectedTotal] as Integer[]
data << group
}
data
}

Without the conversion via as Integer[], the following exception was thrown:

  Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.038 sec <<< FAILURE!
initializationError0(prystasj.junit.LamboMileageTest) Time elapsed: 0.007 sec <<< ERROR!
java.lang.Exception: prystasj.junit.LamboMileageTest.data() must return a Collection of arrays.
Additionally, if I tried to append to data in one statement without the intermediate group variable, a GroovyCastException was thrown. I could wrap the right side of the append with parentheses, but I think this way is easier to read.

Each parameter grouping will be passed to each test method in my test class. Here I will have only one test method, but if I had two, each would be run with [5, 5], and then each would be run with [10, 15], and so on.

The test method:

    @Test void driveSomeDistance() {
println "Parmeters: [miles=$miles, total=$total]"
lambo.drive(miles)
assertEquals "Odomoter reads '$total'", total, lambo.odometer
}

The output from a successful test run:

  Running prystasj.junit.LamboMileageTest
Created Lambo: prystasj.junit.Lambo[odometer=0]
Parmeters: [miles=5, total=5]
Parmeters: [miles=10, total=15]
Parmeters: [miles=20, total=35]
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.173 sec

The entire test class:

    package prystasj.junit

import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
import org.junit.runners.Parameterized.Parameters
import static org.junit.Assert.assertEquals

@RunWith(value=Parameterized)
public class LamboMileageTest {

static Lambo lambo
def total
def miles

LamboMileageTest(milesToDrive, expectedTotal) {
this.miles = milesToDrive
this.total = expectedTotal
}

@BeforeClass static void createLambo() {
lambo = new Lambo()
println "Created Lambo: $lambo"
}

@Parameters static def data() {
def data = []
def expectedTotal = 0
[5, 10, 20].each { miles ->
expectedTotal += miles
def group = [miles, expectedTotal] as Integer[]
data << group
}
data
}

@Test void driveSomeDistance() {
println "Parmeters: [miles=$miles, total=$total]"
lambo.drive(miles)
assertEquals "Odomoter reads '$total'", total, lambo.odometer
}

}

Looking at the nature of a parameterized test class, and at the goal of my tests, I would recommend that this test class only be used for testing the accumulation of miles. If I wanted to test other Lambo functionality or usage, such as a Lambo not being able to drive negative miles, I would create another test class.

Without making use of parameterization, I would of wrote a test class like this:

    public class AlternateLamboMileageTest {

Lambo lambo

@Before void setUp() {
lambo = new Lambo()
}

@Test void starting_with_zero_miles_drive_5() {
lambo.odometer = 0
lambo.drive(5)
assertEquals 5, lambo.odometer
}

@Test void starting_with_five_miles_drive_10() {
lambo.odometer = 5
lambo.drive(10)
assertEquals 15, lambo.odometer
}

@Test void starting_with_fifteen_miles_drive_20() {
lambo.odometer = 15
lambo.drive(20)
assertEquals 35, lambo.odometer
}
}

This test class might be easier to follow though, or at least would of been before I started writing. With this approach however, I need to set the odometer before testing the result of a drive as I can't guarantee the order in which the tests are run. If I could, I could also use a static Lambo instance as before and avoid the odometer settings, or JExample to define test dependencies (which I wrote a bit about here).

This new test class also helps expose a design issue with my Lambo modeling, as unless I was some sort of shady used car salesman, I shouldn't be able to set the odometer once its initialized.

Then again, maybe if I was a shady salesman, I could find my way into a real Lambo... :)

Friday, November 13, 2009

Subversion: Changing Log/Commit Message

I'm posting this one because I only find myself looking it up whenever I need it, but to change the commit message for a particular revision for a Subversion repository, run:
  $ svn propset --revprop svn:log --revision 100 \
"New message, last was wrong." http://svn.mycompany.org/MyProject
This requires that the pre-revprop-change hook script is enabled. More on how to do that can be found here: Implementing Repository Hooks

Bash: Default Variable Values from Command Line Arguments

I wanted to have a bash script that takes one command line argument, but would default to a given value if the argument was ommitted. Here's a simple test script that demonstrates how I can do it:
  #!/bin/bash
VAR=default
if [ -n "$1" ]; then
VAR=$1
fi
echo $VAR
The -n operator tests that a string is not null and is quoted which is considered a good practice.

If I run the script, with no arguments, default is output to the screen, otherwise the argument is displayed.
  prystasj:~$ ./test.sh
default
prystasj:~$ ./test.sh hello
hello