Using JUnit 4 Theories to Test Contracts 5

Posted by Berin Loritsch Tue, 09 Feb 2010 16:08:00 GMT

I’ve been putting off upgrading to JUnit 4 for a while. After all, just how much does it really buy you? It turns out, that JUnit 4 grants you a number of advantages that weren’t available with JUnit 3. One of those features is currently in an experimental phase: Theories. Theories let you specify a bunch of data points, which can be applied to each theory in your class. For example, if you want to test some boundary conditions on a class, you can make it happen easily like this:

@RunWith(Theories.class)
public class VerifyMyAlgorithm {
    @DataPoint
    public static int limLow = 0;

    @DataPoint
    public static int limHigh = 3000;

    @DataPoint
    public static int belowLimLow = limLow - limHigh;

    @DataPoint
    public static int aboveLimHigh = limHigh * 2;

    @DataPoint
    public static int median = (limHigh - limLow) / 2;

    @Theory
    public void verifyTwoParameters(int first, int second) {
        assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
    }
}

I wanted to keep it simple just so you can get the basic idea. Essentially the @RunWith() annotation changes the basic behavior of your test class. It enables the use of the following annotations: @DataPoint, @DataPoints (more on this later), and @Theory. The data points you specified are collected and matched together so that each unique combination of datapoints is applied to the parameters in your theory. If your theory takes one parameter, the theory is run once per data point. If your theory takes two parameters it is run with every unique combination (15 times in this case).

But wait, there’s more! Sometimes we want to generate our datapoints with code. For example, we may need prime numbers up two four significant digits, or we need to initialize our data. In order to do that, we can use the @DataPoints annotation. The return type of your static method will be an array of datapoints. I could rewrite the example above like this:

@RunWith(Theories.class)
public class VerifyMyAlgorithm {
    @DataPoints
    public static int[] attemptLimits() {
        return new int[] {0,3000,-3000,6000,1500};
    }

    @Theory
    public void verifyTwoParameters(int first, int second) {
        assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
    }
}

The DataPoints functionality in concert with the theories are the foundation of what is ncessary to automatically test the contracts of each service. I’ve thrown together a rudimentary class scanner that uses reflection to iterate over classes in the classpath to determine if they match the criteria for the service. For example, you can look at all classes that implement an interface, or extend a base class, or even have an annotation. From that list of classes we can test the contracts of the implementations. The nice aspect of this approach over creating a base test class and extend it manually for each implementation we write, is that it automatically finds the new implementations for you. I have yet to release the class scanner, but you can implement your own pretty well. Here is an example of how it would be used:

@RunWith(Theories.class)
public class EnforceLocakableContracts {
    @DataPoints
    public static Lockable[] collectImplementations() {
        Collection<Class<?>> klasses = new ClassCollector()
                .assignableTo(Locakable.class).recurse().collect();

       Lockable[] implementations = new Lockable[klasses.size()];
       int i = 0;
       for (Class<?> klass : klasses) {
           implementation[i] = klass.newInstance();
           i++;
       }

       return implementations;
    }

    @Theory
    public void lockShouldApplyToOneUser(Lockable lockable) {
        User one = TestSupport.createUser("one");
        User two = TestSupport.createUser("two");

        assertFalse(lockable.isLocked());

        lockable.acquireLock(one);

        assertFalse(lockable.canAccess(two);
    }

    @Theory
    public void lockableShouldBeAccessibleByLocker(Lockable lockable) {
        User one = TestSupport.createUser("test");

        assertFalse(lockable.isLocked());

        lockable.acquerLock(one);

        assertTrue(lockable.canAccess(one);
    }
}

So on and so forth. Just add a new Theory for each aspect of the implementing class you want to test. With this approach, testing implmentations of an interface is essentially future proofinf yourself against lazy programmers. There are some aspects that this approach doesn’t handle well just yet, such as complex setup for each object.

As of JUnit 4.7 there are a few problems, but they are not insurmountable:

  • You see one pass/fail per theory (not per implementation)
  • Failures do not show what implementation failed (fixed in the stacktraces provided by JUnit 4.8.1)
  • The first failure kills future tests. You may have errors in multiple implementations but you won’t be able to find it until you fix the first implementation.

The best thing is to upgrade to JUnit 4.8.1, which is still not available in Maven repositories yet. However, you can also use the System.out approach to find out what the last thing tested was.

There is a feature request for theories to allow you to run them discretely. I.e. each run gets displayed in your test report individually with the parameters supplied in the test name. That will address the shortcomings and make this an even better approach.

Wicket, Spring, Hibernate, Testing... Yeah, it's like that.

Posted by Berin Loritsch Tue, 10 Nov 2009 16:57:00 GMT

I have to deliver a product using Java. Due to politics and approved baselines, etc. I can’t use Ruby on Rails. I’ve done my own thing in the past, and so this time I decided to save some time by incorporating Wicket, Spring, and Hibernate using auto-wiring, and attributes. Getting it all to talk together took the better part of a day, but I got it to deploy and run fine.

I’m an avid unit tester, so I was happy to learn that Wicket has some facilities to make testing a bit easier without forcing you to create your own Servlet mock objects. I was really happy about that, so I decided to try it out. Unfortunately, I ran into a rather nasty roadblock. Spring was complaining about the ContextLoaderListener not being loaded. It’s defined in my web.xml file, but the autowiring requires the spring context to be loaded in a particular location. Finding out how to fix that problem took me the better part of half a day.

The problem is that Wicket hides the ServletContext object away from you. You can’t add values, because the test framework is initialized on construction. Attempts to obtain the ServletContext and manually add the Spring web application context just weren’t working. That’s because it is configured when the WicketTester object is constructed and ignored after that. I needed to be able to inject my own ServletContext with the Spring integration taken care of. I finally figured it out here, because the forums just working for me. Please note that this is tested with Wicket 1.4 and not any of the earlier versions:

final MyApplication app = new MyApplication();
tester = new WicketTester(app) {
    @Override
    public ServletContext newServletContext(final String path) {
        // web context
        ServletContext context = new MockServletContext(app, path);
        // spring context
        XmlWebApplicationContext spring = new XmlWebApplicationContext();
        spring.setServletContext(context);
        // configure spring
        spring.setConfigLocation("classpath:application.xml");

        // put spring where Wicket can find it
        context.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, spring);
        return context;
    }
};

So let me explain what I did. I created an anonymous subclass of the WicketTester class so I could override the factory method “newServletContext” to inject my spring configuration. Done properly, the wicket folks can take care of this by providing a SpringWicketTester subclass in their spring integration library. The SpringWicketTester would let you specify the location of your Spring configuration file, and it would override that method for you. For now, you’ll have to do it yourself.

I put the WicketTester initialization in a subclass of the JUnit TestCase class. That way I don’t have to repeat myself for setting up my Wicket testing.