A mini mocking framework

Andy Balaam
artificialworlds.net/blog

Contents

Cool Code

Mocking

When you are writing tests,

Mocking

// Don't kill a real person
IVictim wood = newMock( IVictim.class );

// Make an axe, and chop
Axe axe = new Axe();
axe.chop( wood );

// Check the wood did get cut
assertMethodCalled( wood, "cut" );

The Tests

public class TestMiniMock
{
    private static interface MyInterface
    {
        public String myMethod( String arg );
    }

//...
}

The Tests

public void Can_make_an_instance()
{
    MyInterface inst = MiniMock.create(
        MyInterface.class );

    assert inst != null;
    assert inst instanceof MyInterface;
}

The Tests

public void Can_track_method_calls()
{
    MyInterface inst = MiniMock.create(
        MyInterface.class );
    String ans = inst.myMethod( "foo" );

    MiniMock.MethodCall[] calls =
        MiniMock.methodCalls( inst );
    assert calls[0].methodName.equals(
        "myMethod" );
    assert calls[0].args[0].equals( "foo" );
}

The Tests

public void Can_specify_return_values()
{
    MyInterface inst = MiniMock.create(
        MyInterface.class,
        "myMethod", "bar" );

    String ans = inst.myMethod( "foo" );

    assert ans.equals( "bar" );
}

Creating objects

public static <T> T create( Class<T> clazz...
{
    InvocationHandler handler = ...

    Object ret = Proxy.newProxyInstance(
        clazz.getClassLoader(),
        new Class[] { clazz },
        handler );

    return (T)ret;
}

Tracking calls

public static <T> T create(...
{
    InvocationHandler handler =
        new TrackingInvocationHandler(...
    ...

Tracking calls

private static class
    TrackingInvocationHandler
    implements InvocationHandler
{
    private List<MethodCall> methodCalls =
        new ArrayList<MethodCall>();

    public Object invoke(
        Object proxy,
        Method method,
        Object[] args ) throws Throwable
    { 
        ...

Tracking calls

public Object invoke( Object proxy,
    Method method, Object[] args )...
{ 
    methodCalls.add(
        new MethodCall(
            method.getName(), args ) );

    return ...

Tracking calls

// Reminder - the test looks like:
MyInterface inst = MiniMock.create(
    MyInterface.class );
String ans = inst.myMethod( "foo" );

MiniMock.MethodCall[] calls =
    MiniMock.methodCalls( inst );

Tracking calls

public static MethodCall[] methodCalls(
    Object proxyObject )
{
    Proxy proxy = (Proxy)proxyObject;
    TrackingInvocationHandler handler =
        (TrackingInvocationHandler)(
            Proxy.getInvocationHandler(
                proxy ) );

    return handler.methodCalls.toArray(
        new MethodCall[0] );
}

Returning values

// Reminder - the test looks like:

MyInterface inst = MiniMock.create(
    MyInterface.class, "myMethod", "bar" );

String ans = inst.myMethod( "foo" );

assert ans.equals( "bar" );

Returning values

... class TrackingInvocationHandler ...
{
    private final 
        Map<String, Object> methRets;

    public TrackingInvocationHandler(
        Object... methodsAndReturnValues )
    {

Returning values

methRets = new HashMap<String, Object>();
for( int i = 0; ...; i += 2 )
{
    methRets.put(
        (String)methodsAndReturnValues[ i ],
        methodsAndReturnValues[ i + 1 ]
    );
}

Returning values

public Object invoke(...
{ 
    methodCalls.add( ...

    return methRets.get(
        method.getName() );
}

More info

Videos youtube.com/user/ajbalaam
Twitter @andybalaam
Blog artificialworlds.net/blog
Projects artificialworlds.net