- Revision
- 592
- Author
- sirenian
- Date
- 2006-11-29 12:17:40 -0600 (Wed, 29 Nov 2006)
Log Message
[EK] More mock stuff. This will need tidying up at some point.
Modified Paths
- trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ClassMockObjectBehaviour.java
- trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/UsingClassMockBehaviour.java
- trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ClassMockObject.java
- trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/UsingClassMock.java
Added Paths
Diff
Modified: trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ClassMockObjectBehaviour.java (591 => 592)
--- trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ClassMockObjectBehaviour.java 2006-11-29 17:03:19 UTC (rev 591) +++ trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ClassMockObjectBehaviour.java 2006-11-29 18:17:40 UTC (rev 592) @@ -4,6 +4,7 @@ import jbehave.core.Block; import jbehave.core.Ensure; +import jbehave.core.exception.PendingException; import jbehave.core.minimock.UsingMiniMock; import jbehave.core.mock.Mock; @@ -18,17 +19,39 @@ } } - private static class AClassWithNoConstructors {} + private static class AClassWithNoDeclaredConstructors {} public static class AClassWithAComplexConstructor { - public AClassWithAComplexConstructor(String anObject, int primitive, char primitive2, Object[] array) { - anObject.compareTo("What happens if the argument is null?"); + public AClassWithAComplexConstructor( + String anObject, + int primitive, + char primitive2, + Object[] array) { + anObject.compareTo("A string"); int i = primitive + array.length; + i++; // just to stop the unused warnings } - } + public static class AClassWithAReallyNastyConstructor { + public AClassWithAReallyNastyConstructor( + String anObject, + int primitive, + char primitive2, + Object[] array, + AClassWithNoConstructorsAtAll someEnum) { + anObject.compareTo(someEnum.toString()); + int i = primitive + array.length; + i++; // just to stop the unused warnings + } + } + + public static class AClassWithNoConstructorsAtAll { + public static final Object INSTANCE = new AClassWithNoConstructorsAtAll(); + private AClassWithNoConstructorsAtAll() {} + } + public void shouldCreateClassObjectThatCanBeCastToTheCorrectType() { Mock mock = ClassMockObject.mockClass(AClass.class, "bar"); Ensure.that(mock instanceof AClass); @@ -49,7 +72,7 @@ public void shouldThrowAnIllegalArgumentExceptionIfClassHasNoConstructors() throws Exception { Ensure.throwsException(IllegalArgumentException.class, new Block() { public void run() throws Exception { - ClassMockObject.mockClass(AClassWithNoConstructors.class, "bar"); + ClassMockObject.mockClass(AClassWithNoDeclaredConstructors.class, "bar"); } }); } @@ -62,8 +85,37 @@ Object actual = ((HashMap)mock).get("a key"); ensureThat(expected, eq(actual)); } + + public void shouldBeAbleToProvideDefaultInstancesForConstructorClasses() { + Mock mock = ClassMockObject.mockClass( + AClassWithAReallyNastyConstructor.class, "foo", + new Class[] { + String.class, + int.class, + char.class, + Object[].class, + AClassWithNoConstructorsAtAll.class}, + new Object[] { + "", + Integer.valueOf(0), + Character.valueOf(' '), + new Object[0], + AClassWithNoConstructorsAtAll.INSTANCE}); + } - public void shouldBeAbleToMockClassesWithConstructorArgs() { - Mock mock = ClassMockObject.mockClass(AClassWithAComplexConstructor.class, "foo"); + public void shouldBeAbleToMockMostClassesWithConstructorArgs() { + ClassMockObject.mockClass(AClassWithAComplexConstructor.class, "foo"); } + + public void shouldRethrowNullPointerExceptionsWithSuggestionWhenConstructorFails() throws Exception { + ensureThrows(IllegalArgumentException.class, new Block() { + public void run() throws Exception { + ClassMockObject.mockClass(AClassWithAReallyNastyConstructor.class, "foo"); + } + }); + } + + public void shouldBeAbleToProvideMinimalProblemClassConstructorsAndHaveTheFactoryFillInTheRest() { + throw new PendingException(); + } }
Added: trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ConstructorFactoryBehaviour.java (0 => 592)
--- trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ConstructorFactoryBehaviour.java (rev 0) +++ trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/ConstructorFactoryBehaviour.java 2006-11-29 18:17:40 UTC (rev 592) @@ -0,0 +1,15 @@ +package jbehave.extensions.classmock; + +import jbehave.core.exception.PendingException; + +public class ConstructorFactoryBehaviour { + + public void shouldConstructStuff() { + // TODO: The ConstructorFactory behaviour was pulled out of the ClassMockObject. + // I haven't got round to writing the behaviour yet. + // It may be worth injecting this into UsingClassMock; that way users + // can provide default instantiations of eg: GlyphType. + + throw new PendingException(); + } +}
Modified: trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/UsingClassMockBehaviour.java (591 => 592)
--- trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/UsingClassMockBehaviour.java 2006-11-29 17:03:19 UTC (rev 591) +++ trunk/extensions/classmocks/src/behaviour/jbehave/extensions/classmock/UsingClassMockBehaviour.java 2006-11-29 18:17:40 UTC (rev 592) @@ -6,6 +6,8 @@ import jbehave.core.minimock.UsingMiniMock; import jbehave.core.mock.Mock; import jbehave.extensions.classmock.UsingClassMock; +import jbehave.extensions.classmock.ClassMockObjectBehaviour.AClassWithAReallyNastyConstructor; +import jbehave.extensions.classmock.ClassMockObjectBehaviour.AClassWithNoConstructorsAtAll; public class UsingClassMockBehaviour extends UsingMiniMock { @@ -21,7 +23,46 @@ ensureThat(expected, eq(actual)); } + public void shouldBeAbleToMockClassesProvidingConstructorArgs() { + Object expected = new Object(); + + Mock mock = classMock.mock(AClassWithAReallyNastyConstructor.class, "foo", + new Class[] { + String.class, + int.class, + char.class, + Object[].class, + AClassWithNoConstructorsAtAll.class}, + new Object[] { + "", + Integer.valueOf(0), + Character.valueOf(' '), + new Object[0], + AClassWithNoConstructorsAtAll.INSTANCE}); + mock.expects("getSomething").will(returnValue(expected)); + + Object actual = ((AClassWithAReallyNastyConstructor)mock).getSomething(); + ensureThat(expected, eq(actual)); + } + public void shouldBeAbleToStubClasses() { throw new PendingException(); - } + } + + public static class AClassWithAReallyNastyConstructor { + public AClassWithAReallyNastyConstructor( + String anObject, + int primitive, + char primitive2, + Object[] array, + AClassWithNoConstructorsAtAll someEnum) { + anObject.compareTo(someEnum.toString()); + int i = primitive + array.length; + i++; // just to stop the unused warnings + } + + public Object getSomething() { + return null; + } + } }
Modified: trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ClassMockObject.java (591 => 592)
--- trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ClassMockObject.java 2006-11-29 17:03:19 UTC (rev 591) +++ trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ClassMockObject.java 2006-11-29 18:17:40 UTC (rev 592) @@ -8,6 +8,7 @@ import jbehave.core.minimock.MiniMockObject; import jbehave.core.mock.ExpectationRegistry; import jbehave.core.mock.Mock; +import net.sf.cglib.core.CodeGenerationException; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; @@ -19,8 +20,13 @@ */ class ClassMockObject extends MiniMockObject { - private ClassMockObject(Class type, String name) { + private final Class[] constructorArgClasses; + private final Object[] constructorArgs; + + private ClassMockObject(Class type, String name, Class[] constructorArgClasses, Object[] constructorArgs) { super(type, name); + this.constructorArgClasses = constructorArgClasses; + this.constructorArgs = constructorArgs; } /** get the mocked instance */ @@ -29,12 +35,21 @@ enhancer.setClassLoader(getType().getClassLoader()); enhancer.setSuperclass(getType()); enhancer.setCallback(new ExpectationHandlerDelegate()); - Class[] constructorArgClasses = getConstructorArgClasses(getType()); - Object[] constructorArgs = createConstructorArgsFor(constructorArgClasses); return enhancer.create(constructorArgClasses, constructorArgs); } - static Mock mockClass(final Class type, final String name) { + public static Mock mockClass(Class type, String name) { + Class[] constructorArgClasses = getConstructorArgClasses(type); + Object[] constructorArgs = createConstructorArgsFor(constructorArgClasses); + return createMockClass(type, name, constructorArgClasses, constructorArgs); + + } + + public static Mock mockClass(final Class type, final String name, Class[] constructorArgClasses, Object[] constructorArgs) { + return createMockClass(type, name, constructorArgClasses, constructorArgs); + } + + private static Mock createMockClass(final Class type, final String name, final Class[] constructorArgClasses, final Object[] constructorArgs) { if (type.getDeclaringClass() != null && !Modifier.isStatic(type.getModifiers())) { throw new IllegalArgumentException("cannot mock non-static inner class " + type.getName()); } @@ -44,7 +59,7 @@ enhancer.setClassLoader(Mock.class.getClassLoader()); enhancer.setInterfaces(new Class[]{Mock.class, ExpectationRegistry.class}); enhancer.setCallback(new MethodInterceptor() { - final ClassMockObject mock = new ClassMockObject(type, name); + final ClassMockObject mock = new ClassMockObject(type, name, constructorArgClasses, constructorArgs); public Object intercept(Object thisProxy, Method method, Object[] args, MethodProxy superProxy) throws Throwable { @@ -60,73 +75,64 @@ } }); - Class[] constructorArgClasses = getConstructorArgClasses(type); - Object[] constructorArgs = createConstructorArgsFor(constructorArgClasses); - - return (Mock) enhancer.create(constructorArgClasses, constructorArgs); + try { + return (Mock) enhancer.create(constructorArgClasses, constructorArgs); + } catch (CodeGenerationException e) { + // Does this in Eclipse, but... + if (e.getCause() instanceof NullPointerException) { + throw caughtANullPointer(type, e); + } else { + throw e; + } + } catch (NullPointerException e) { + // For some reason, it does this on the command line + // (calls Enhancer.nextInstance() instead of Enhancer.firstInstance() ) + throw caughtANullPointer(type, e); + } } + private static IllegalArgumentException caughtANullPointer(final Class type, Throwable e) { + return new IllegalArgumentException("Caught a NullPointerException while trying to mock a " + type + ". This could be caused " + + "because a constructor argument that couldn't be instantiated was used in the constructor. Have you tried " + + "providing constructor arguments?", e); + } + private static Class[] getConstructorArgClasses(Class type) { - Constructor[] constructors = type.getConstructors(); + Constructor[] constructors = type.getDeclaredConstructors(); if (constructors.length == 0) { - throw new IllegalArgumentException("Cannot construct class " + type); + constructors = type.getConstructors(); } + if (constructors.length == 0) { + throw new IllegalArgumentException("No constructors available for class " + type); + } + if (Modifier.isPrivate(constructors[0].getModifiers())) { + try { + constructors[0].setAccessible(true); + } catch (SecurityException e) { + throw new IllegalArgumentException("No constructors available for class " + type); + } + } return constructors[0].getParameterTypes(); } private static Object[] createConstructorArgsFor(Class[] constructorArgClasses) { Object[] args = new Object[constructorArgClasses.length]; + ConstructorFactory constructorFactory = new ConstructorFactory(); for (int i = 0; i < args.length; i++) { Class clazz = constructorArgClasses[i]; try { - Object result = construct(clazz); + Object result = constructorFactory.construct(clazz); args[i] = result; } catch (Exception e) { - throw new RuntimeException("Could not mock class " + constructorArgClasses[i] + " at index " + i, e); + throw new RuntimeException("Could not create constructor argument for class " + constructorArgClasses[i] + " at index " + i, e); } } return args; } - private static Object construct(Class clazz) throws InstantiationException, IllegalAccessException { - Object result = null; - if (clazz.isPrimitive()) { - result = constructPrimitive(clazz); - } else if (clazz.isArray()) { - result = new Object[] {}; - } else if (Modifier.isFinal(clazz.getModifiers())) { - result = clazz.newInstance(); - } else { - result = new UsingClassMock().mock(clazz); - } - return result; - } - - private static Object constructPrimitive(Class clazz) { - if (clazz == byte.class) { - return new Byte((byte) 0); - } else if (clazz == boolean.class) { - return Boolean.FALSE; - } else if (clazz == char.class) { - return new Character(' '); - } else if (clazz == double.class) { - return new Double(0); - } else if (clazz == float.class) { - return new Float(0); - } else if (clazz == int.class) { - return new Integer(0); - } else if (clazz == long.class) { - return new Long(0L); - } else if (clazz == short.class) { - return new Short((short) 0); - } else { - throw new IllegalArgumentException("Never heard of a primitive called " + clazz + " before. "); - } - } - private class ExpectationHandlerDelegate extends ExpectationHandler implements MethodInterceptor { public Object intercept(Object thisProxy, Method method, Object[] args, MethodProxy superProxy) throws Throwable { return this.invoke(thisProxy, method, args);
Added: trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ConstructorFactory.java (0 => 592)
--- trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ConstructorFactory.java (rev 0) +++ trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/ConstructorFactory.java 2006-11-29 18:17:40 UTC (rev 592) @@ -0,0 +1,47 @@ +package jbehave.extensions.classmock; + +import java.lang.reflect.Modifier; + +public class ConstructorFactory { + + static Object construct(Class clazz) throws InstantiationException, IllegalAccessException { + Object result = null; + if (clazz.isPrimitive()) { + result = constructPrimitive(clazz); + } else if (clazz.isArray()) { + result = new Object[] {}; + } else if (Modifier.isFinal(clazz.getModifiers())) { + result = clazz.newInstance(); + } else if (clazz.isInterface()) { + result = new UsingClassMock().mock(clazz); + } else if (clazz.getConstructors().length == 0) { + result = null; // and hope they're not using it + } else { + result = new UsingClassMock().mock(clazz); + } + return result; + } + + private static Object constructPrimitive(Class clazz) { + if (clazz == byte.class) { + return new Byte((byte) 0); + } else if (clazz == boolean.class) { + return Boolean.FALSE; + } else if (clazz == char.class) { + return Character.valueOf(' '); + } else if (clazz == double.class) { + return Double.valueOf(0); + } else if (clazz == float.class) { + return Float.valueOf(0); + } else if (clazz == int.class) { + return Integer.valueOf(0); + } else if (clazz == long.class) { + return Long.valueOf(0L); + } else if (clazz == short.class) { + return Short.valueOf((short) 0); + } else { + throw new IllegalArgumentException("Never heard of a primitive called " + clazz + " before. "); + } + } + +}
Modified: trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/UsingClassMock.java (591 => 592)
--- trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/UsingClassMock.java 2006-11-29 17:03:19 UTC (rev 591) +++ trunk/extensions/classmocks/src/java/jbehave/extensions/classmock/UsingClassMock.java 2006-11-29 18:17:40 UTC (rev 592) @@ -8,4 +8,12 @@ protected Mock createMock(Class type, String name) { return ClassMockObject.mockClass(type, name); } + + public Mock mock(Class type, Class[] argTypes, Object[] args) { + return mock(type, type.getName(), argTypes, args); + } + + public Mock mock(Class type, String name, Class[] argTypes, Object[] args) { + return ClassMockObject.mockClass(type, name, argTypes, args); + } }
To unsubscribe from this list please visit:
