Permalink
Browse files

Automatically create mocks, states and sequences for annotated fields.

  • Loading branch information...
1 parent a6b196a commit 3246dfecb6dcb3f5b74b072edd76ce40de92ce76 npryce committed Dec 3, 2009
@@ -0,0 +1,12 @@
+package org.jmock.auto;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Retention(RUNTIME)
+@Target(FIELD)
+public @interface Auto {
+}
@@ -0,0 +1,13 @@
+package org.jmock.auto;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+
+@Retention(RUNTIME)
+@Target(FIELD)
+public @interface Mock {
+}
@@ -0,0 +1,70 @@
+package org.jmock.auto.internal;
+
+import java.lang.reflect.Field;
+
+import org.jmock.Mockery;
+import org.jmock.Sequence;
+import org.jmock.States;
+import org.jmock.auto.Auto;
+import org.jmock.auto.Mock;
+
+
+public class Mockomatic {
+ private final Mockery mockery;
+
+ public Mockomatic(Mockery mockery) {
+ this.mockery = mockery;
+ }
+
+ public void fillIn(Object object) {
+ for (Class<?> type = object.getClass(); type != Object.class; type = type.getSuperclass()) {
+ Field[] fields = type.getDeclaredFields();
+ for (Field field: fields) {
+ if (field.isAnnotationPresent(Mock.class)) {
+ autoMock(object, field);
+ }
+ else if (field.isAnnotationPresent(Auto.class)) {
+ if (field.getType() == States.class) {
+ autoInstantiateStates(field, object);
+ }
+ else if (field.getType() == Sequence.class) {
+ autoInstantiateSequence(field, object);
+ }
+ else {
+ throw new IllegalStateException("cannot auto-instantiate field of type " + field.getType().getName());
+ }
+ }
+ }
+ }
+ }
+
+ private void autoInstantiateStates(Field field, Object object) {
+ try {
+ field.setAccessible(true);
+ field.set(object, mockery.states(field.getName()));
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException("cannot auto-instantiate States field " + field.getName(), e);
+ }
+ }
+
+ private void autoInstantiateSequence(Field field, Object object) {
+ try {
+ field.setAccessible(true);
+ field.set(object, mockery.sequence(field.getName()));
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException("cannot auto-instantiate Sequence field " + field.getName(), e);
+ }
+ }
+
+ private void autoMock(Object object, Field field) {
+ try {
+ field.setAccessible(true);
+ field.set(object, mockery.mock(field.getType(), field.getName()));
+ }
+ catch (IllegalAccessException e) {
+ throw new IllegalStateException("cannot auto-mock field " + field.getName(), e);
+ }
+ }
+}
@@ -5,6 +5,7 @@
import org.jmock.States;
import org.jmock.api.Imposteriser;
import org.jmock.api.MockObjectNamingScheme;
+import org.jmock.auto.internal.Mockomatic;
import org.jmock.internal.ExpectationBuilder;
/**
@@ -18,23 +19,29 @@
public abstract class MockObjectTestCase extends VerifyingTestCase {
private final Mockery context = new Mockery();
- {
- context.setExpectationErrorTranslator(JUnit3ErrorTranslator.INSTANCE);
- addVerifier(new Runnable() {
- public void run() {
- context.assertIsSatisfied();
- }
- });
- }
-
public MockObjectTestCase() {
super();
+ initialise();
}
public MockObjectTestCase(String name) {
super(name);
+ initialise();
}
+ private void initialise() {
+ context.setExpectationErrorTranslator(JUnit3ErrorTranslator.INSTANCE);
+
+ addVerifier(new Runnable() {
+ public void run() {
+ context.assertIsSatisfied();
+ }
+ });
+
+ Mockomatic mockomatic = new Mockomatic(context);
+ mockomatic.fillIn(this);
+ }
+
public Mockery context() {
return context;
}
@@ -3,6 +3,7 @@
import java.lang.reflect.Field;
import org.jmock.Mockery;
+import org.jmock.auto.internal.Mockomatic;
import org.junit.runner.Runner;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
@@ -27,6 +28,14 @@ public JMock(Class<?> testClass) throws InitializationError {
}
@Override
+ protected Object createTest() throws Exception {
+ Object test = super.createTest();
+ Mockomatic mockomatic = new Mockomatic(mockeryOf(test));
+ mockomatic.fillIn(test);
+ return test;
+ }
+
+ @Override
protected Statement possiblyExpectingExceptions(FrameworkMethod method, Object test, Statement next) {
return verify(method, test, super.possiblyExpectingExceptions(method, test, next));
}
@@ -45,6 +54,10 @@ public void evaluate() throws Throwable {
};
}
+ protected void assertMockeryIsSatisfied(Object test) {
+ mockeryOf(test).assertIsSatisfied();
+ }
+
protected Mockery mockeryOf(Object test) {
try {
Mockery mockery = (Mockery)mockeryField.get(test);
@@ -58,10 +71,6 @@ protected Mockery mockeryOf(Object test) {
}
}
- protected void assertMockeryIsSatisfied(Object test) {
- mockeryOf(test).assertIsSatisfied();
- }
-
static Field findMockeryField(Class<?> testClass) throws InitializationError {
Field mockeryField = null;
@@ -22,6 +22,11 @@ public NamedSequence(String name) {
this.name = name;
}
+ @Override
+ public String toString() {
+ return name;
+ }
+
public void constrainAsNextInSequence(InvocationExpectation expectation) {
int index = elements.size();
elements.add(expectation);
@@ -3,6 +3,7 @@
import junit.framework.TestCase;
import org.jmock.test.acceptance.junit4.testdata.DerivedJUnit4TestThatDoesNotSatisfyExpectations;
+import org.jmock.test.acceptance.junit4.testdata.JUnit4TestThatAutoInstantiatesMocks;
import org.jmock.test.acceptance.junit4.testdata.JUnit4TestThatCreatesNoMockery;
import org.jmock.test.acceptance.junit4.testdata.JUnit4TestThatCreatesTwoMockeries;
import org.jmock.test.acceptance.junit4.testdata.JUnit4TestThatDoesNotCreateAMockery;
@@ -65,6 +66,11 @@ public void testDetectsNonPublicBeforeMethodsCorrectly() {
"Method before() should be public", listener.failure.getMessage());
}
+ public void testAutoInstantiatesMocks() {
+ runTest(JUnit4TestThatAutoInstantiatesMocks.class);
+ assertTestSucceeded();
+ }
+
private void assertTestSucceeded() {
if (listener.failure != null) {
fail("test should have passed but reported failure: " + listener.failure.getMessage());
@@ -0,0 +1,23 @@
+package org.jmock.test.acceptance.junit4.testdata;
+
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+import org.jmock.Sequence;
+import org.jmock.States;
+import org.jmock.auto.Auto;
+import org.jmock.auto.Mock;
+import org.junit.Test;
+
+public class JUnit4TestThatAutoInstantiatesMocks extends BaseClassWithMockery {
+ @Mock Runnable runnable;
+ @Auto States states;
+ @Auto Sequence sequence;
+
+ @Test
+ public void fieldsHaveBeenAutoInstantiated() {
+ assertThat(runnable, notNullValue());
+ assertThat(states, notNullValue());
+ assertThat(sequence, notNullValue());
+ }
+}
@@ -0,0 +1,86 @@
+package org.jmock.test.unit.auto.internal;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.startsWith;
+import static org.junit.Assert.assertThat;
+import junit.framework.TestCase;
+
+import org.jmock.Mockery;
+import org.jmock.Sequence;
+import org.jmock.States;
+import org.jmock.auto.Auto;
+import org.jmock.auto.Mock;
+import org.jmock.auto.internal.Mockomatic;
+import org.jmock.test.acceptance.MockedType;
+
+public class MockomaticTests extends TestCase {
+ Mockery mockery = new Mockery();
+ Mockomatic mockomatic = new Mockomatic(mockery);
+
+ public static class ObjectWithPublicAndPrivateFields {
+ public @Mock MockedType publicMock;
+ private @Mock MockedType privateMock;
+
+ public MockedType privateMock() { return privateMock; }
+ }
+
+ public void testCreatesMockObjectsNamedAfterTheField() {
+ ObjectWithPublicAndPrivateFields example = new ObjectWithPublicAndPrivateFields();
+
+ mockomatic.fillIn(example);
+
+ assertThat("created public mock",
+ example.publicMock, notNullValue());
+ assertThat("named public mock after field",
+ example.publicMock.toString(), equalTo("publicMock"));
+
+ assertThat("created private mock",
+ example.privateMock(), notNullValue());
+ assertThat("named private mock after field",
+ example.privateMock().toString(), equalTo("privateMock"));
+ }
+
+ public static class BaseClass {
+ public @Mock MockedType mockInBaseClass;
+ }
+ public static class DerivedClass extends BaseClass {
+ public @Mock MockedType mockInDerivedClass;
+ }
+
+ public void testCreatesMockObjectsInAllClassesInInheritanceHierarchy() {
+ DerivedClass example = new DerivedClass();
+ mockomatic.fillIn(example);
+
+ assertThat("created mock in base class", example.mockInBaseClass, notNullValue());
+ assertThat("created mock in derived class", example.mockInDerivedClass, notNullValue());
+ }
+
+ public static class WantsStates {
+ public @Auto States stateMachine;
+ }
+
+ public void testCreatesStateMachinesNamedAfterTheField() {
+ WantsStates example = new WantsStates();
+ mockomatic.fillIn(example);
+
+ assertThat("created state machine",
+ example.stateMachine, notNullValue());
+ assertThat("named state machine after field",
+ example.stateMachine.toString(), startsWith("stateMachine "));
+ }
+
+ public static class WantsSequence {
+ public @Auto Sequence aSequence;
+ }
+
+ public void testCreatesSequencesNamedAfterTheField() {
+ WantsSequence example = new WantsSequence();
+ mockomatic.fillIn(example);
+
+ assertThat("created sequence",
+ example.aSequence, notNullValue());
+ assertThat("named sequence after field",
+ example.aSequence.toString(), equalTo("aSequence"));
+ }
+}

0 comments on commit 3246dfe

Please sign in to comment.