Showing posts with label JUnit. Show all posts
Showing posts with label JUnit. Show all posts

Monday, 2 March 2009

Using Groovy and GUnit to test Java 'privately'

Normally if I am writing a class and find I need access to a private field in order to test correctly then I take it as a smell - something not quite right with the design. But every now and then I fail to find a way to change the design without corrupting its intent.

Now I know you can use reflection in Java to access private fields, but its a bit clunky. But recently I have been reading about Groovy, and found that it gives you full access to all your Java objects, but allows you to ignore private specifiers.
So as an example, say I have a counter class, one which allows users to get the next value of the counter, but cannot provide anyway of altering the counter for that would invalidate its purpose.

package net.usersource.example;

import java.util.concurrent.atomic.AtomicInteger;

public class Counter {
   private final AtomicInteger counter;

   public Counter() {
       counter = new AtomicInteger(0);
   }

   public int getNext() {
       return ensuringValueDoesNotRollIntoNegativeValues(counter.incrementAndGet());
   }

   private int ensuringValueDoesNotRollIntoNegativeValues(int value) {
       if( value < 0 ) {
            if( counter.compareAndSet(value, 0) ) {
                return 0;
            }
            else {
                return getNext();
            }
        }
        return value;
    }
}
Now, how do I test this without calling getNext() until it reaches Integer.MAX_VALUE? Do you know just how big MAX_VALUE really is, and how long that will take? Thats not really an option - so I need somehow to change the underlying counter.

Now I could do that with reflection, but I can do it much simpler using Groovy (which if you look closely, especially the way I have written it, looks like java) ...

package net.usersource.example

import org.junit.Test;
import org.junit.Assert;
import java.lang.Integer;

public class CounterTest {
    @Test
    public void verifyFirstObtainedCounterValueIsOne() {
        Counter c = new Counter();
        Assert.assertEquals( c.getNext(), 1 );
    }

    @Test
    public void verifyThatAtMaxIntTheNextValueIsZero() {
        Counter c = new Counter();
        c.counter.set(Integer.MAX_VALUE-1);
        Assert.assertEquals( Integer.MAX_VALUE, c.getNext() );
        Assert.assertEquals( 0, c.getNext() );
    }
}
This was TDD'd so I only knew the solution thanks to the test.

Unfortunately I have found some issues with the Eclipse Groovy Plugin - its auto-completion is a bit zealous in someways (wants to try and match everything bringing your machine to a crawl) and ignorant in others (not matching in imports). Also the GUnit results were reporting exceptions the line above the line with the issue - which caused me some confusion initially, and I need to investigate that further.

But to me this is a neat way of testing Java code, by stepping out of Java to something that makes it easier, whilst staying on the JVM.

Saturday, 28 February 2009

Is JUnit Named Right?

When JUnit first appeared, it was perfectly named, however as time has moved on so has its use.

Jim Webber's recent blog post (here) made a very valid point about when a Unit Test is not really a Unit Test. Test and Behavioural Driven Design push JUnit into an area that is not adequately described as unit testing. Driving a design through testing in this manner means you aren't (at least initially) writing unit tests.

I've also used JUnit in the past to do things which are decidedly not unit testing, its a useful framework to control other sorts of testing. And on more than one occasion have had to explain to a member of management why I used a free unit testing framework instead of using their big expensive shiny testing metaframework thingy.

I am not suggesting that Kent and Eric should rush out and re-release their work under a different name. It was (to me at least) interesting to reflect on naming, and how things progress over time, so once perfectly named entities may gradually slip to the almost perfect. And it is also a reminder that not quite perfect risks gradually slipping to the not relevant at all.

What the perfect name would be today, I don't know, I think the name is still perfect enough.