tags:

views:

338

answers:

3

We're finally migrating our unit test code base from JUnit 3 to JUnit 4. We also make heavy use of JMock 2.

With JUnit 3, JMock provides a useful base class for your tests (MockObjectTestCase), which as well as itself being s subclass of Junit's TestCase, it handles various housekeeping duties regarding the mock framework. It makes life pretty easy for the test class.

Now with JUnit4, JMock provides no such support. Your test class has to manually create a Mockery object, it has to remember to use the correct test runner annotation, and must delegate all mock-related operations to the mockery. In short, it puts far more responsibility on the test class than was needed for JUnit 3 tests.

Now I appreciate that part of JUnit4's charm is there being no need to subclass something, but this JMock situation seems like a step backwards, and makes porting from 3 to 4 rather more work than should be necessary.

Am I missing something? Is there actually a nice way to write my JUnit4/Jmock2 test classes without manually adding all that plumbing to every class? I could write my own support base class, of course, but it seems such an obvious omission from the JMock2 API, I have to wonder if I've missed the point.


Edit: here's the source code of what the optional support class would look like:

@RunWith(JMock.class)
public class JMockSupport {

    protected final Mockery mockery = new Mockery();

    protected void checking(ExpectationBuilder expectations) {
     mockery.checking(expectations);
    }

    protected <T> T mock(Class<T> typeToMock) {
     return mockery.mock(typeToMock);
    }

    protected <T> T mock(Class<T> typeToMock, String name) {
     return mockery.mock(typeToMock, name);
    }

    protected Sequence sequence(String name) {
     return mockery.sequence(name);
    }

    protected void setDefaultResultForType(Class<?> type, Object result) {
     mockery.setDefaultResultForType(type, result);
    }

    protected void setImposteriser(Imposteriser imposteriser) {
     mockery.setImposteriser(imposteriser);
    }

    protected void setNamingScheme(MockObjectNamingScheme namingScheme) {
     mockery.setNamingScheme(namingScheme);
    }

    protected States states(String name) {
     return mockery.states(name);
    }
}

This contains all of the methods that the JUnit3 MockObjectTestCase class defined, which just echo to the mockery. The @RunWith annotation is there also, to avoid the possibility of forgetting to add it to your test class.

+1  A: 

No. There is no such support.

The test base class in JMock 1 caused a lot of problems, because you can only extend a single class, and so people couldn't use JMock with other test frameworks that also defined base classes. That's why we went with delegation rather than inheritance in JMock2.

That said, you might be able to use the MockObjectTestCase class from JMock2's JUnit3 support library as long as you annotate your class with @RunWith(JMock.class). But I haven't tried.

There has been a request for an "auto-mocking" JUnit4 runner that will create the context and mock objects for you by automagical reflection. Some people like this, others really don't like it. If you want this feature, vote for the issue in the JMock JIRA.

Nat
Oh hullo Nat, didn't expect to find you here :) I've modified my question to include the source of the suggested base class. Note that it's entirely optional, but does make life a little bit easier.
skaffman
+2  A: 

I've done this migration too, and it is a pain. I can understand why they've binned the base class mechanism - I was trying to juggle JMock base classes with Spring JUnit-enabled base classes, and that obvious doesn't work.

Once I embarked on this migration, one area I found for 'optimisation' was creating appropriate Expectation base classes encapsulating common operations on your mock objects, rather than creating a new Expectation object (and instance) for every test. That will save you a little grief.

Brian Agnew
Think you've commented the wrong message?
Brian Agnew
+1  A: 

There are also problems with having base classes. In previous versions, I suffered from trying to combine base classes from different test frameworks. That's why we went to composition over inheritance. It'll be interesting to see what we can do with the new @Rule structure.

Steve Freeman
I agree that composition is immeasurably more flexible, but inheritance brings convenience. It's nice to able to pick.
skaffman
For the record, I've implemented an @Rule context in the JMock respository
Steve Freeman