Wednesday, December 5, 2007

Using JMock with Scala

While working on the Java version of Jiva earlier this year, I used JMock pretty heavily to test all of the non-deterministic code that is integral to the workings of a Genetic Algorithm.

When I ported Jiva to Scala, I did not really know if I could continue to use JMock for my testing needs. As it turned out, JMock worked out very well for testing Scala code.

I could go in and try to explain some of the ways in which JMock is used within Jiva, but I'm afraid that will introduce a lot of Genetic Algorithms related terminology, and will potentially divert attention from the main point of this post. So I will instead demonstrate the usage of JMock with Scala via a very simple (and contrived) example. If you want to see JMock in action within Jiva, you are welcome to browse the Subversion repository for the project:
http://jiva-ng.googlecode.com/svn/trunk/src/test/scala/net/kogics/jiva/operators/

Back to the simple example...

Let's say we have a Class-under-test (Cut) in our code, and a Helper class that is used by Cut:

class Helper {
def help = "helpfulResult"
def helpWith(problem: String) = problem + " whatever help for String problem"
def helpWith(problem: int) = problem + " whatever help for int problem"
}

class Cut(helper: Helper) {
def doWork: String = {
helper.help + " processed"
}

def doWork(problem: String): String = {
helper.helpWith(problem) + " str processed"
}

def doWork(problem: int): String = {
helper.helpWith(problem) + " int processed"
}
}

The Helper class has three different helper methods with different signatures. Cut also has three methods that delegate to the corresponding Helper methods. The idea here is that we want to test the Cut class by mocking out the helper used by Cut.

Let's jump right into the Test class (you will need to enable Java generics support in Scala for this to work; more on that later):

import junit.framework.TestCase
import junit.framework.Assert._

import org.jmock.Mockery
import org.jmock.lib.legacy.ClassImposteriser
import net.kogics.jiva.util.mock.SExpectations
import org.jmock.Expectations._

class TestCut extends TestCase {

val context = new Mockery() {
{
// we want to be able to mock classes
setImposteriser(ClassImposteriser.INSTANCE);
}
};

def test1 = {
// create the mock
val helper = (context.mock(classOf[Helper])).asInstanceOf[Helper]

// setup mock return values
val noOpHelpReturn = "mockHelp"
val strHelpReturn = "mockHelp stringResult"
val intHelpReturn = "mockHelp intResult"

// set Expectations
context.checking(
new SExpectations {
one(helper).help
will(returnValue(noOpHelpReturn));

exactly(1).of(helper).helpWith(withArg(any(classOf[String])))
will(returnValue(strHelpReturn));

atLeast(1).of(helper).helpWith(withArg(any(classOf[int])))
will(returnValue(intHelpReturn));
})

// setup the class under test with the mock
val cut = new Cut(helper)


// exercise the class under test
// and verify results
val result = cut.doWork
assertEquals(result, noOpHelpReturn + " processed")

val result2 = cut.doWork("task")
assertEquals(result2, strHelpReturn + " str processed")

val result3 = cut.doWork(3)
assertEquals(result3, intHelpReturn + " int processed")

// make sure that the expectations on the mock were satisfied
context.assertIsSatisfied
}
}
This should be pretty straightforward for those of you who are familiar with JMock, except for a couple of things:
  • We set up Expectations using something called SExpectations.
  • We specify method paramater expectations using a method called withArg, as opposed to the more familiar with method provided by JMock
Both of these items are driven by the same reason: with is a reserved word in Scala. To get around this, I created a subclass of Expectations:

package net.kogics.jiva.util.mock;

import org.hamcrest.Matcher;
import org.jmock.Expectations;

public class SExpectations extends Expectations {
public <T> T withArg(Matcher<T> matcher) {
return super.with(matcher);
}
}
SExpectations provides a method called withArg that just calls into the standard with method provided within its superclass by JMock. So essentially this class functions as an adapter that allows JMock parameter matching to be used by code written in Scala.

Update: as pointed out by James Iry in his comment, we can solve this problem right within Scala-land, with the help of identifier quoting using backtics. Here's SExpectations in Scala:

class SExpectations extends Expectations {
def withArg[T](matcher: Matcher[T]): T = super.`with`(matcher)
}
This version of SExpectations can be used in the scala test code in exactly the same way as the Java version. Alternatively, the with method in Expectations can be called directly in the test code with the help of backticks. Both these approaches are shown in the sample code below:

// set Expectations
context.checking(
new SExpectations {
one(helper).help
will(returnValue(noOpHelpReturn));

exactly(1).of(helper).helpWith(withArg(any(classOf[String])))
will(returnValue(strHelpReturn));

atLeast(1).of(helper).helpWith(`with`(any(classOf[int])))
will(returnValue(intHelpReturn));
})
That's about it, actually. To reiterate: if you know how to use JMock (with Java), it should be pretty straightforward for you to start using it with Scala.

Some additional comments:
  • To work with JMock, you need to use Scala with the -Ygenerics and -target:jvm-1.5 options.
  • I used JUnit 3 for my Scala unit tests because the Scala Eclipse plugin does not quite support annotations. The standalone/command-line version of Scala supports annotations just fine, so it should be very possible to use JMock/JUnit 4 with Scala
  • I suppose I could have used MockObjectTestCase as a base class for my Tests. I just wanted to have more control over things as I worked through the Scala/JMock integration
  • I used the CGLib based Legacy ClassImposteriser because I needed to mock system classes for testing Jiva code
  • Versions used - Scala: 2.6.0, JMock: 2.4.0

Saturday, December 1, 2007

Announcing Jiva, a Genetic Algorithms Toolkit written in Scala

Earlier this week, I put out an initial release (version 0.1.5) of Jiva, a Genetic Algorithms (GA) toolkit that I have been working on.

Jiva is hosted on Google Code:
http://code.google.com/p/jiva-ng/

Jiva is written in Scala, a very powerful OO/Functional hybrid language that runs on the Java Platform:
http://www.scala-lang.org/

Jiva started out life earlier this year as a GA toolkit written in Java. I decided to port it to Scala because I figured that this might be a good way for me to (a) learn Scala, and (b) have some fun in the process. And I think this has turned out to be mostly true (especially the fun part!).

I should point out that despite its early-release status, Jiva is pretty functional (http://code.google.com/p/jiva-ng/wiki/FeatureSet ). If you are interested in playing with Genetic Algorthms, I encourage you to give Jiva a try. If you run into problems or have questions, please let me know.

Some quick links:
Download, Build and Run Jiva: http://code.google.com/p/jiva-ng/wiki/JivaQuickStart
Defining and Running GA Problems: http://code.google.com/p/jiva-ng/wiki/DefiningAndRunningGAProblems
Samples: http://code.google.com/p/jiva-ng/wiki/SampleProblems

Other tools used by Jiva:
Sant: for builds (http://code.google.com/p/sant/).
JMock: for testing of Scala code (http://www.jmock.org/).
New Scala Eclipse Plugin (http://lamp.epfl.ch/~mcdirmid/scala.update).
Much4.us: for Backlog/Task Management (http://much4.us/much4/).

Many thanks to the authors of these fine tools! I used them pretty heavily while working on Jiva, and they all held up very well and provided extremely useful functionality.