How Clojure improved my Groovy
Dave Thomas and Andy Hunt have been saying for years that you should learn new programming languages. Doing so improves your skill in any language, whether you use the new language or not. I believed this with some skepticism, but decided nonetheless to start learning Clojure after reading The Pragmatic Programmer.
I’m reading and working through Programming Clojure, and one of the examples in the book looked like this:
(use '[clojure.contrib.str-utils :only (str-join)]) (str-join "-" ["hello", "clojure"])
Then, a week or so later, as I was working on some Groovy code, I saw something like the following:
def elements = ["It", "works", "on", "my", "machine!"] def sb = new StringBuilder() elements.eachWithIndex { element, index -> if (index == 0) { sb.append(element) } else { sb.append(" ") sb.append(element) } } assert sb.toString() == "It works on my machine!"
And I thought, “man, this code would be so much better in Clojure!” Since I can’t use Clojure for my job (yet), I set out to find a better way to do this in Groovy. I decided to look for something similar to Clojure’s str-join for Groovy, and I found the join() method. It does exactly what I needed!
def elements = ["It", "works", "on", "my", "machine!"] def sb = new StringBuilder() sb.append(elements.join(" ")) assert sb.toString() == "It works on my machine!"
And so, I discovered for myself that Dave and Andy were right. Knowing Clojure (specifically, the clojure-contrib library) helped me to write better Groovy code.
SnakeYAML and Groovy
I had some fun playing with SnakeYAML and Groovy the other day. Below are some of my results. If you want to run this in your groovyConsole, you need to download SnakeYAML and add the snakeyaml-<version>.jar to your classpath.
From YAML to Groovy:
import org.yaml.snakeyaml.Yaml Yaml yaml = new Yaml() def obj = yaml.load(""" a: 1 b: 2 c: - aaa - bbb""") assert obj.a == 1 assert obj.b == 2 assert obj.c == ["aaa", "bbb"]
From Groovy to YAML:
import org.yaml.snakeyaml.Yaml def map = [name: "Pushkin", aliases: ['P', 'Push']] Yaml yaml = new Yaml() String output = yaml.dump(map) assert output == '''name: Pushkin aliases: [P, Push] '''
Isn’t YAML so much simpler and cleaner than XML? Why aren’t more of us using YAML with Java and Groovy?
Top 5 posts of 2009
Our top 5 posts of 2009 by page views:
- [].inject(“Groovy”){}
- Who needs a mock framework?
- Mocking and stubbing in Groovy with ‘with’
- Java 6 and Maven on Mac OS X Leopard
- Liferay Portal is pretty snazzy
Here are a few posts from 2008 (our first year) that were still popular in 2009:
- Cookies in Firefox and Internet Explorer
- Groovy’s each method
- The Executor Framework
- Chain those Crazy Groovy Closures with Currying, Case Study: Mimicking Intercepting Filter
- Groovy + JUnit + Ant
And here’s my favorite post of all time: What’s the most fun you’ve ever had… programming?
Thanks for reading!
Upgrading Maven from 2.0.x to 2.1.x – profiles.xml
When we tried to upgrade from Maven 2.0.x to 2.1.x, our build broke, indicating we had a problem with our profiles.xml. I had a hard time finding out what’s supposed to be in the profiles.xml, since I couldn’t find a reference to the schema on the Maven website. I finally found a profiles.xml in one of our projects at work that referenced the profiles XML schema, which can be found here:
http://maven.apache.org/xsd/profiles-1.0.0.xsd
I’m still not sure if the schema changed from 2.0.x to 2.1.x, but I know that using <profiles/>
as the root element worked in 2.0.x but did not work in 2.1.x. When you upgrade to 2.1.x, make sure the root element in your profiles.xml is <profilesXml/>
. Actually, you can make the change to your profiles.xml before upgrading to Maven 2.1.x and then upgrade when you’re ready.
Mocking and stubbing in Groovy with ‘with’
I often use metaClass in Groovy to create mocks or stubs in my unit tests. Recently I’ve discovered that the with method makes this a bit simpler.
This:
Foo.metaClass.with { doSomething = { return true } anotherThing = {foo -> assertEquals 42, foo } }
is slightly shorter than this:
Foo.metaClass.doSomething = { return true } Foo.metaClass.anotherThing = {foo -> assertEquals 42, foo }
and less code means less bugs.
[Update: For more explanation of why the with method is so great, read this post.]
[Update: And for another way to write your mocks and stubs, see my previous post on mocking with Groovy.]
Integrating Unit Tests
There’s been a lot of talk about TDD, Responsibility Driven Design, and what constitutes a unit test versus an integration test this week at work. It got me thinking about the definitions. I used to think it was very clear cut. Unit tests isolate a single class, integration test don’t.
Is the line so simple though? What about a unit test that tests a method that uses other methods within the same class to get its work done. Is testing the results of that method a unit test?
For example:
// Say I wanted to test the method.
String makeDeposit( String xmlData ) {
def transaction = parseRequestXml( request )
def receipt = bankBusinessObject.deposit( transaction )
return makeResponseXml( receipt )
}
To be a unit test, I should at least mock the bankBusinessObject, and should probably ask the Mock to fail my test if the deposit method isn’t called.
But, it seems that to be a true unit test for makeDeposit, I should also mock parseRequestXml and makeResponseXml. And, for the makeDeposit unit test, rather than test that you get the expected xml repsponse data given the controlled input data, just ensure that makeDeposit calls parseRequestXml to parse it, bankBuisinessObject.deposit to act on it, and makeResponseXml to marshal the results.
I did some brain storming and came up with a new classification scheme for organizing my tests.
Unit
- Macro Unit Tests – Tests isolated to a single class. This is where you’d see testing of methods which call other methods to get some of their work done.
- Micro Unit Tests – Tests isolated to a single method. Completely test the method in isolation, mocking as necessary. If you work in a language like Groovy and want to unit test a method that calls or depends on other methods in the same class, mock the dependent methods individually and have the mocks fail the test if they’re not called as expected, in the order expected. I’m not sure if that is possible in Java. (Maybe with some sort of test harness with AOP involved to intercept the method calls during the test??)
Integration
- Standard Integration Tests – Integration tests that span other methods, classes, depend on other services, databases, etc.
- Requirements Tests – Tests for those when you want to test a specific requirement or bug fix. Very helpful when something changes later that breaks the bug you fixed a few releases back. Or when the customer opens a ticket asking for functionality that contradicts a previous request.
Thoughts?
Java 6 and Maven on Mac OS X Leopard
This morning I was having problems with Java and Maven on my Mac. I had my JAVA_HOME set to /Library/Java/Home, which was pointing to Java 6. When I ran mvn -v, Maven told me it was pointing to Java 5. I couldn’t figure out what was wrong because running java -version showed me Java 6.
The solution, which I found in this post, was to set my JAVA_HOME to the actual place where Java is installed, which is something like:
/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home
This worked for me, but YMMV.
Who needs a mock framework?
In Groovy, you can create mocks & stubs using native language features, so you can forget about learning a mock framework.
class Author { def getName() { return 'Josh Brown' } def isFamous() { return false } }
Let’s stub the isFamous method:
def author = [isFamous: {true}] as Author assert 'Josh Brown' == author.getName() assert author.isFamous()
(If you haven’t already, stick the above code in your groovyConsole, run it, and play around with it.)
When mocking and stubbing are so easy to do with the language itself, using a mock framework is overkill.
[Update: Bob Martin recently wrote an excellent post about Manual Mocking.]
Recent Comments