diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java b/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java
new file mode 100644
index 0000000000..e81243119f
--- /dev/null
+++ b/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005-2012
+ Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.util.test.junitrules;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.rules.ExternalResource;
+import org.springframework.util.ReflectionUtils;
+
+/**
+ * A JUnit rule designed to help with the automatic revert of test objects with mocked fields.
+ * This is intended to be used when writing test code and you wish to set a mock object on a spring singleton bean.
+ * By mocking out fields on spring singletons beans, if you don't remember to revert them to their original values, then
+ * any subsequent tests that expect to see 'proper' services plugged in, may fail when they are instead given a mock.
+ *
+ *
+ * Example usage:
+ *
+ * public class YourTestClass
+ * {
+ * // Declare the rule.
+ * @Rule public final TemporaryMockOverride mockOverride = new TemporaryMockOverride();
+ *
+ * @Test public void aTestMethod()
+ * {
+ * // Get a singleton bean from the spring context
+ * FooService fooService = appContext.getBean("fooService", FooService.class);
+ *
+ * // Create a mocked service (this uses Mockito, but that's not required for this rule to work)
+ * BarService mockedBarService = mock(BarService.class);
+ *
+ * // Don't do this as you're just replacing the original barService: fooService.setBarService(mockedBarService);
+ * // Instead do this:
+ * mockOverride.setTemporaryField(fooService, barService, mockedBarService);
+ *
+ * // Go ahead and use the FooService in test code, whilst relying on a mocked BarService behind it.
+ * // After the rule has completed, the original BarService which spring injected into the FooService will be reset.
+ * }
+ * }
+ *
+ *
+ * @author Neil Mc Erlean
+ * @since Odin
+ */
+public class TemporaryMockOverride extends ExternalResource
+{
+ private static final Log log = LogFactory.getLog(TemporaryMockOverride.class);
+
+ private List pristineFieldValues = new ArrayList();
+
+ @Override protected void before() throws Throwable
+ {
+ // Intentionally empty
+ }
+
+ @Override protected void after()
+ {
+ // For all objects that have been tampered with, we'll revert them to their original state.
+ for (int i = pristineFieldValues.size() - 1; i >= 0; i-- )
+ {
+ FieldValueOverride override = pristineFieldValues.get(i);
+
+ if (log.isDebugEnabled())
+ {
+ log.debug("Reverting mocked field '" + override.fieldName + "' on object " + override.objectContainingField + " to original value '" + override.fieldPristineValue + "'");
+ }
+
+ // Hack into the Java field object
+ Field f = ReflectionUtils.findField(override.objectContainingField.getClass(), override.fieldName);
+ ReflectionUtils.makeAccessible(f);
+ // and revert its value.
+ ReflectionUtils.setField(f, override.objectContainingField, override.fieldPristineValue);
+ }
+ }
+
+ public void setTemporaryField(Object objectContainingField, String fieldName, Object fieldValue)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Overriding field '" + fieldName + "' on object " + objectContainingField + " to new value '" + fieldValue + "'");
+ }
+
+ // Extract the pristine value of the field we're going to mock.
+ Field f = ReflectionUtils.findField(objectContainingField.getClass(), fieldName);
+
+ if (f == null)
+ {
+ final String msg = "Object of type '" + objectContainingField.getClass().getSimpleName() + "' has no field named '" + fieldName + "'";
+ if (log.isDebugEnabled())
+ {
+ log.debug(msg);
+ }
+ throw new IllegalArgumentException(msg);
+ }
+
+ ReflectionUtils.makeAccessible(f);
+ Object pristineValue = ReflectionUtils.getField(f, objectContainingField);
+
+ // and add it to the list.
+ pristineFieldValues.add(new FieldValueOverride(objectContainingField, fieldName, pristineValue));
+
+ // and set it on the object
+ ReflectionUtils.setField(f, objectContainingField, fieldValue);
+ }
+
+ private static class FieldValueOverride
+ {
+ public FieldValueOverride(Object objectContainingField, String fieldName, Object pristineValue)
+ {
+ this.objectContainingField = objectContainingField;
+ this.fieldName = fieldName;
+ this.fieldPristineValue = pristineValue;
+ }
+
+ public final Object objectContainingField;
+ public final String fieldName;
+ public final Object fieldPristineValue;
+ }
+}
diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java b/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java
new file mode 100644
index 0000000000..201bcbb67c
--- /dev/null
+++ b/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2005-2012 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+
+package org.alfresco.util.test.junitrules;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Test class for {@link TemporaryMockOverride}.
+ *
+ * @author Neil McErlean
+ * @since Odin
+ */
+public class TemporaryMockOverrideTest
+{
+ private static final String REAL_DATA = "Hello";
+ private static final String MOCKED_DATA = "--";
+
+ private final FooService realFooService = new FooServiceImpl();
+ private final BarService realBarService = new BarServiceImpl();
+ private final AbcService realAbcService = new AbcServiceImpl();
+
+ @Before public void init()
+ {
+ FooServiceImpl fooServiceImpl = (FooServiceImpl)realFooService;
+ fooServiceImpl.setBarService(realBarService);
+ fooServiceImpl.setAbcService(realAbcService);
+ }
+
+ @Test public void mockFieldsWithinServiceAndThenEnsureTheProperFieldValuesAreRestoredAfterCleanup() throws Throwable
+ {
+ TemporaryMockOverride mockRule = new TemporaryMockOverride();
+ mockRule.before();
+
+ assertEquals("Original BarService giving wrong data.", REAL_DATA, realFooService.getBarService().getString());
+ assertEquals("Original AbcService giving wrong data.", REAL_DATA, realFooService.getAbcService().getString());
+
+ BarService mockedBarService = mock(BarService.class);
+ when(mockedBarService.getString()).thenReturn(MOCKED_DATA);
+
+ AbcService mockedAbcService = mock(AbcService.class);
+ when(mockedAbcService.getString()).thenReturn(MOCKED_DATA);
+
+ FooServiceImpl fooServiceWithMockedServices = new FooServiceImpl();
+ // We'll start it off with the 'correct' values
+ fooServiceWithMockedServices.setBarService(realBarService);
+ fooServiceWithMockedServices.setAbcService(realAbcService);
+
+ // ...and then set the mocked values via the rule, which will remember the old values and revert them for us automatically.
+ mockRule.setTemporaryField(fooServiceWithMockedServices, "barService", mockedBarService);
+ mockRule.setTemporaryField(fooServiceWithMockedServices, "abcService", mockedAbcService);
+
+
+ assertEquals("Mocked BarService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getBarService().getString());
+ assertEquals("Mocked AbcService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getAbcService().getString());
+
+ mockRule.after();
+
+ // Now it should all be magically reverted.
+ assertEquals("BarService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getBarService().getString());
+ assertEquals("AbcService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getAbcService().getString());
+ }
+
+ @Test(expected=IllegalArgumentException.class) public void mockNonExistentFieldsWithinService() throws Throwable
+ {
+ TemporaryMockOverride mockRule = new TemporaryMockOverride();
+ mockRule.before();
+
+ assertEquals("Original BarService giving wrong data.", REAL_DATA, realFooService.getBarService().getString());
+
+ BarService mockedBarService = mock(BarService.class);
+ when(mockedBarService.getString()).thenReturn(MOCKED_DATA);
+
+ FooServiceImpl fooServiceWithMockedServices = new FooServiceImpl();
+ // We'll start it off with the 'correct' values
+ fooServiceWithMockedServices.setBarService(realBarService);
+
+ // ...and then set an illegal mocked value via the rule.
+ mockRule.setTemporaryField(fooServiceWithMockedServices, "noSuchService", mockedBarService);
+
+
+ assertEquals("Mocked BarService giving wrong data.", MOCKED_DATA, fooServiceWithMockedServices.getBarService().getString());
+
+ mockRule.after();
+
+ // Now it should all be magically reverted.
+ assertEquals("BarService giving wrong data.", REAL_DATA, fooServiceWithMockedServices.getBarService().getString());
+ }
+
+ public interface FooService { public BarService getBarService(); public AbcService getAbcService(); }
+
+ public class FooServiceImpl implements FooService
+ {
+ private BarService barService;
+ private AbcService abcService;
+
+ public BarService getBarService() { return this.barService; }
+ public void setBarService(BarService barService) { this.barService = barService; }
+ public AbcService getAbcService() { return this.abcService; }
+ public void setAbcService(AbcService abcService) { this.abcService = abcService; }
+ }
+
+ public interface BarService { public String getString(); }
+
+ public class BarServiceImpl implements BarService { public String getString() { return REAL_DATA; } }
+
+ public interface AbcService { public String getString(); }
+
+ public class AbcServiceImpl implements AbcService { public String getString() { return REAL_DATA; } }
+}
+