Skip to content

Instantly share code, notes, and snippets.

@darrend
Created June 11, 2010 16:37
Show Gist options
  • Save darrend/434731 to your computer and use it in GitHub Desktop.
Save darrend/434731 to your computer and use it in GitHub Desktop.
wrote this before Chuck told me about mockito; i guess this has the advantage of no dependencies at all
import java.util.List;
import java.util.LinkedList;
import java.util.Arrays;
import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Mocks interfaces with a series of pre-determined calls and preloaded return values.
*
* @param <T> Type of interface to be mocked
*/
public class WindUpMockBuilder<T> {
private T windingProxy;
private Class clazz;
private List<Invocation> invocations=new LinkedList<Invocation>();
private Object returnValue=null;
private Throwable throwable=null;
public WindUpMockBuilder(Class clazz) {
windingProxy = (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new WindingMechanism());
this.clazz = clazz;
}
/**
* Returns an implementation of the interface that stores invocations in a list of expected calls to be made on the mock during testing
*
* @return special implementation of interface that remembers the invocations made on it
*/
public T expectCall() {
return expectCallReturning(null);
}
/**
* Returns an implementation of the interface that stores invocations in a list of expected calls and return values to be made on the mock during testing
*
* @param returnValue what will be returned when the method is later called during testing
* @return special implementation of interface that remembers the invocations made on it
*/
public T expectCallReturning(Object returnValue) {
this.returnValue = returnValue;
return windingProxy;
}
public T expectCallThrowing(Throwable throwable) {
this.throwable = throwable;
return windingProxy;
}
/**
*
* @return implementation of the mocked interface configured with the predetermined calls and responses
*/
public T getTestableMock() {
return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{clazz}, new UnwindingMechanism());
}
private class WindingMechanism implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Invocation invocation = new Invocation(method, args, returnValue, throwable);
invocations.add(invocation);
returnValue=null;
throwable=null;
return invocation.returnValue;
}
}
private class UnwindingMechanism implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("windupmock_builder")) {
return WindUpMockBuilder.this;
}
if(invocations.isEmpty()) {
throw new IllegalStateException("Method unexpected");
}
Invocation expectedInvocation = invocations.remove(0);
if(!expectedInvocation.method.equals(method)) {
throw new IllegalStateException("Method unexpected");
}
if(expectedInvocation.throwable!=null) {
throw throwable;
}
return expectedInvocation.returnValue;
}
}
private class Invocation {
private Method method;
private Object[] args;
private Object returnValue;
private Throwable throwable;
private Invocation(Method method, Object[] args, Object returnValue, Throwable throwable) {
this.method = method;
this.args = args;
this.returnValue = returnValue;
this.throwable = throwable;
}
public void setReturnValue(Object returnValue) {
this.returnValue = returnValue;
}
@Override
public String toString() {
return "Invocation{" +
"method=" + method +
", args=" + (args == null ? null : Arrays.asList(args)) +
'}';
}
}
public static void main(String[] args) {
WindUpMockBuilder<List<Integer>> listBuilder = new WindUpMockBuilder<List<Integer>>(List.class);
listBuilder.expectCallReturning(true).add(3);
listBuilder.expectCallReturning(false).isEmpty();
listBuilder.expectCall().clear();
listBuilder.expectCallReturning(true).isEmpty();
List<Integer> intList = listBuilder.getTestableMock();
assert intList.add(3);
assert !intList.isEmpty();
intList.clear();
assert intList.isEmpty();
// gonna get weird
listBuilder.expectCallReturning(false).isEmpty();
assert !intList.isEmpty();
boolean threwException=false;
try {
intList.add(15); // not expecting this call
} catch (IllegalStateException ex) {
threwException = true;
}
assert threwException;
}
}
@darrend
Copy link
Author

darrend commented Jun 11, 2010

Chuck Fouts pointed out that Mockito is an existing framework that does similar stuff and more http://mockito.org/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment