All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.unitils.easymock.EasyMockModule Maven / Gradle / Ivy

There is a newer version: 3.4.6
Show newest version
/*
 * Copyright 2008,  Unitils.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.unitils.easymock;

import org.easymock.classextension.internal.MocksClassControl;
import org.easymock.internal.MocksControl;
import org.easymock.internal.ReplayState;
import org.unitils.core.Module;
import org.unitils.core.TestListener;
import org.unitils.core.UnitilsException;
import org.unitils.easymock.annotation.AfterCreateMock;
import org.unitils.easymock.annotation.Mock;
import org.unitils.easymock.annotation.RegularMock;
import org.unitils.easymock.util.*;
import org.unitils.reflectionassert.ReflectionComparatorMode;
import org.unitils.util.PropertyUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import static org.easymock.internal.MocksControl.MockType.DEFAULT;
import static org.easymock.internal.MocksControl.MockType.NICE;
import static org.unitils.reflectionassert.ReflectionComparatorMode.*;
import static org.unitils.util.AnnotationUtils.getFieldsAnnotatedWith;
import static org.unitils.util.AnnotationUtils.getMethodsAnnotatedWith;
import static org.unitils.util.ModuleUtils.getAnnotationPropertyDefaults;
import static org.unitils.util.ModuleUtils.getEnumValueReplaceDefault;
import static org.unitils.util.ReflectionUtils.invokeMethod;
import static org.unitils.util.ReflectionUtils.setFieldValue;

/**
 * Module for testing with mock objects using EasyMock.
 * 

* Mock creation is simplified by automatically inserting EasyMock generated mocks for fields annotated with the * {@link Mock} annotation. *

* All methods annotated with {@link AfterCreateMock} will be called when a mock object was created. This provides * you with a hook method for custom handling of the mock (e.g. adding the mocks to a service locator repository). * A method can only be called if it has following signature void myMethod(Object mock, String name, Class type). *

* Mocks can also be created explicitly * todo javadoc *

* Switching to the replay state and verifying expectations of all mocks (including the mocks created with * the createMock() method can be done by calling * the {@link #replay()} and {@link #verify()} methods. * * @author Filip Neven * @author Tim Ducheyne */ public class EasyMockModule implements Module { /* Property key for configuring whether verify() is automatically called on every mock object after each test method execution */ public static final String PROPKEY_AUTO_VERIFY_AFTER_TEST_ENABLED = "EasyMockModule.autoVerifyAfterTest.enabled"; /* All created mocks controls */ private List mocksControls; /* Map holding the default configuration of the mock annotations */ private Map, Map> defaultAnnotationPropertyValues; /* Indicates whether verify() is automatically called on every mock object after each test method execution */ private boolean autoVerifyAfterTestEnabled; /** * Initializes the module */ @SuppressWarnings("unchecked") public void init(Properties configuration) { mocksControls = new ArrayList(); defaultAnnotationPropertyValues = getAnnotationPropertyDefaults(EasyMockModule.class, configuration, RegularMock.class, Mock.class); autoVerifyAfterTestEnabled = PropertyUtils.getBoolean(PROPKEY_AUTO_VERIFY_AFTER_TEST_ENABLED, configuration); } /** * No after initialization needed for this module */ public void afterInit() { } /** * Creates the listener for plugging in the behavior of this module into the test runs. * * @return the listener */ public TestListener getTestListener() { return new EasyMockTestListener(); } /** * Creates an EasyMock mock object of the given type. *

* An instance of the mock control is stored, so that it can be set to the replay/verify state when * {@link #replay()} or {@link #verify()} is called. * * @param the type of the mock * @param mockType the class type for the mock, not null * @param invocationOrder the order setting, not null * @param calls the calls setting, not null * @return a mock for the given class or interface, not null */ public T createRegularMock(Class mockType, InvocationOrder invocationOrder, Calls calls) { // Get anotation arguments and replace default values if needed invocationOrder = getEnumValueReplaceDefault(RegularMock.class, "invocationOrder", invocationOrder, defaultAnnotationPropertyValues); calls = getEnumValueReplaceDefault(RegularMock.class, "calls", calls, defaultAnnotationPropertyValues); MocksControl mocksControl; if (Calls.LENIENT == calls) { mocksControl = new MocksClassControl(NICE); } else { mocksControl = new MocksClassControl(DEFAULT); } // Check order if (InvocationOrder.STRICT == invocationOrder) { mocksControl.checkOrder(true); } mocksControls.add(mocksControl); return mocksControl.createMock(mockType); } /** * todo javadoc *

* Creates an EasyMock mock instance of the given type (class/interface). The type of mock is determined * as follows: *

* If returns is set to LENIENT, a nice mock is created, else a default mock is created * If arguments is lenient a lenient control is create, else an EasyMock control is created * If order is set to strict, invocation order checking is enabled * * @param the type of the mock * @param mockType the type of the mock, not null * @param invocationOrder the order setting, not null * @param calls the calls setting, not null * @param order todo * @param dates todo * @param defaults todo * @return a mockcontrol for the given class or interface, not null */ public T createMock(Class mockType, InvocationOrder invocationOrder, Calls calls, Order order, Dates dates, Defaults defaults) { // Get anotation arguments and replace default values if needed invocationOrder = getEnumValueReplaceDefault(Mock.class, "invocationOrder", invocationOrder, defaultAnnotationPropertyValues); calls = getEnumValueReplaceDefault(Mock.class, "calls", calls, defaultAnnotationPropertyValues); order = getEnumValueReplaceDefault(Mock.class, "order", order, defaultAnnotationPropertyValues); dates = getEnumValueReplaceDefault(Mock.class, "dates", dates, defaultAnnotationPropertyValues); defaults = getEnumValueReplaceDefault(Mock.class, "defaults", defaults, defaultAnnotationPropertyValues); List comparatorModes = new ArrayList(); if (Order.LENIENT == order) { comparatorModes.add(LENIENT_ORDER); } if (Dates.LENIENT == dates) { comparatorModes.add(LENIENT_DATES); } if (Defaults.IGNORE_DEFAULTS == defaults) { comparatorModes.add(IGNORE_DEFAULTS); } LenientMocksControl mocksControl; if (Calls.LENIENT == calls) { mocksControl = new LenientMocksControl(NICE, comparatorModes.toArray(new ReflectionComparatorMode[0])); } else { mocksControl = new LenientMocksControl(DEFAULT, comparatorModes.toArray(new ReflectionComparatorMode[0])); } // Check order if (InvocationOrder.STRICT == invocationOrder) { mocksControl.checkOrder(true); } mocksControls.add(mocksControl); return mocksControl.createMock(mockType); } /** * Replays all mock controls. */ public void replay() { for (MocksControl mocksControl : mocksControls) { mocksControl.replay(); } } /** * Resets all mock controls. */ public void reset() { for (MocksControl mocksControl : mocksControls) { mocksControl.reset(); } } /** * This method makes sure {@link org.easymock.internal.MocksControl#verify} method is called for every mock mock object * that was injected to a field annotated with {@link Mock}, or directly created by calling * {@link #createRegularMock(Class, InvocationOrder, Calls)} or * {@link #createMock(Class, InvocationOrder, Calls, Order, Dates, Defaults)}. *

* If there are mocks that weren't already switched to the replay state using {@link MocksControl#replay()}} or by * calling {@link org.unitils.easymock.EasyMockUnitils#replay()}, this method is called first. */ public void verify() { for (MocksControl mocksControl : mocksControls) { if (!(mocksControl.getState() instanceof ReplayState)) { mocksControl.replay(); } mocksControl.verify(); } } /** * Creates and sets a mock for all {@link RegularMock} annotated fields. *

* The * todo javadoc * method is called for creating the mocks. Ones the mock is created, all methods annotated with {@link AfterCreateMock} will be called passing the created mock. * * @param testObject the test, not null */ protected void createAndInjectRegularMocksIntoTest(Object testObject) { Set mockFields = getFieldsAnnotatedWith(testObject.getClass(), RegularMock.class); for (Field mockField : mockFields) { Class mockType = mockField.getType(); RegularMock regularMockAnnotation = mockField.getAnnotation(RegularMock.class); Object mockObject = createRegularMock(mockType, regularMockAnnotation.invocationOrder(), regularMockAnnotation.calls()); setFieldValue(testObject, mockField, mockObject); callAfterCreateMockMethods(testObject, mockObject, mockField.getName(), mockType); } } //todo javadoc protected void createAndInjectMocksIntoTest(Object testObject) { Set mockFields = getFieldsAnnotatedWith(testObject.getClass(), Mock.class); for (Field mockField : mockFields) { Class mockType = mockField.getType(); Mock mockAnnotation = mockField.getAnnotation(Mock.class); Object mockObject = createMock(mockType, mockAnnotation.invocationOrder(), mockAnnotation.calls(), mockAnnotation.order(), mockAnnotation.dates(), mockAnnotation.defaults()); setFieldValue(testObject, mockField, mockObject); callAfterCreateMockMethods(testObject, mockObject, mockField.getName(), mockType); } } /** * Calls all {@link AfterCreateMock} annotated methods on the test, passing the given mock. * These annotated methods must have following signature void myMethod(Object mock, String name, Class type). * If this is not the case, a runtime exception is called. * * @param testObject the test, not null * @param mockObject the mock, not null * @param name the field(=mock) name, not null * @param type the field(=mock) type */ protected void callAfterCreateMockMethods(Object testObject, Object mockObject, String name, Class type) { Set methods = getMethodsAnnotatedWith(testObject.getClass(), AfterCreateMock.class); for (Method method : methods) { try { invokeMethod(testObject, method, mockObject, name, type); } catch (InvocationTargetException e) { throw new UnitilsException("An exception occurred while invoking an after create mock method.", e); } catch (Exception e) { throw new UnitilsException("Unable to invoke after create mock method. Ensure that this method has following signature: " + "void myMethod(Object mock, String name, Class type)", e); } } } /** * Test listener that handles the mock creation and injection. */ protected class EasyMockTestListener extends TestListener { /** * Before the test is executed this calls {@link EasyMockModule#createAndInjectRegularMocksIntoTest(Object)} to * create and inject all mocks on the class. */ @Override public void beforeTestSetUp(Object testObject, Method testMethod) { // Clear all previously created mocks controls mocksControls.clear(); createAndInjectRegularMocksIntoTest(testObject); createAndInjectMocksIntoTest(testObject); } /** * After each test is executed this calls {@link EasyMockModule#verify()} to verify the recorded behavior * of all created mocks. */ @Override public void afterTestMethod(Object testObject, Method testMethod, Throwable throwable) { if (autoVerifyAfterTestEnabled && throwable == null) { verify(); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy