Title: [jbehave] [592] trunk/extensions/classmocks/src/java/jbehave/extensions/classmock: [EK] More mock stuff.

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:

http://xircles.codehaus.org/manage_email

Reply via email to