Created
June 11, 2010 16:37
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Chuck Fouts pointed out that Mockito is an existing framework that does similar stuff and more http://mockito.org/