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

org.unitils.easymock.util.LenientMocksControl Maven / Gradle / Ivy

The 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.util;

import java.util.List;

import org.easymock.IAnswer;
import org.easymock.IArgumentMatcher;
import org.easymock.internal.IMocksControlState;
import org.easymock.internal.Invocation;
import org.easymock.internal.LastControl;
import org.easymock.internal.MocksControl;
import org.easymock.internal.Range;
import org.easymock.internal.RecordState;
import org.unitils.reflectionassert.ReflectionComparatorMode;

/**
 * An EasyMock mock control that uses the reflection argument matcher for all arguments of a method invocation.
 * 

* No explicit argument matcher setting is needed (or allowed). This control will automatically report * lenient reflection argument matchers. These matchers can apply some leniency when comparing expected and actual * argument values. *

* Setting the {@link ReflectionComparatorMode#IGNORE_DEFAULTS} mode will for example ignore all fields that * have default values as expected values. E.g. if a null value is recorded as argument it will not be checked when * the actual invocation occurs. The same applies for inner-fields of object arguments that contain default java values. *

* Setting the {@link ReflectionComparatorMode#LENIENT_DATES} mode will ignore the actual date values of arguments and * inner fields of arguments. It will only check whether both dates are null or both dates are not null. The actual * date and hour do not matter. *

* Setting the {@link ReflectionComparatorMode#LENIENT_ORDER} mode will ignore the actual order of collections and * arrays arguments and inner fields of arguments. It will only check whether they both contain the same elements. * * @author Tim Ducheyne * @author Filip Neven * @see ReflectionComparatorMode * @see org.unitils.reflectionassert.ReflectionComparator */ public class LenientMocksControl extends MocksControl { /***/ private static final long serialVersionUID = -4612378998272988410L; /* The interceptor that wraps the record state */ private InvocationInterceptor invocationInterceptor; /** * Creates a default (no default returns and no order checking) mock control. * * @param modes the modes for the reflection argument matcher */ public LenientMocksControl(ReflectionComparatorMode... modes) { this(org.easymock.MockType.DEFAULT, modes); } /** * Creates a mock control.

    *
  • Default mock type: no default return values and no order checking
  • *
  • Nice mock type: returns default values if no return value set, no order checking
  • *
  • Strict mock type: no default return values and strict order checking
  • *
* * @param type the EasyMock mock type * @param modes the modes for the reflection argument matcher */ public LenientMocksControl(org.easymock.MockType type, ReflectionComparatorMode... modes) { super(type); this.invocationInterceptor = new InvocationInterceptor(modes); } /** * Overriden to be able to replace the record behavior that going to record all method invocations. * The interceptor will make sure that reflection argument matchers will be reported for the * arguments of all recorded method invocations. * * @return the state, wrapped in case of a RecordState */ @Override public IMocksControlState getState() { IMocksControlState mocksControlState = super.getState(); if (mocksControlState instanceof RecordState) { invocationInterceptor.setRecordState((RecordState) mocksControlState); return invocationInterceptor; } return mocksControlState; } /** * A wrapper for the record state in easy mock that will intercept the invoke method * so that it can install reflection argument matchers for all arguments of the recorded method invocation. *

* The old easy mock way of having a single argument matcher for all arguments has been deprecated. Since * EasyMock 2 each argument should have its own matcher. We however want to avoid having to set all * matchers to the reflection argument matcher explicitly. * Because some of the methods are declared final and some classes explicitly cast to subtypes, creating a wrapper * seems to be the only way to be able to intercept the matcher behavior. */ private class InvocationInterceptor implements IMocksControlState { /* The wrapped record state */ private RecordState recordState; /* The modes for the reflection argument matchers */ private ReflectionComparatorMode[] modes; /** * Creates an interceptor that will create reflection argument matchers for all arguments of all recorded * method invocations. * * @param modes the modes for the reflection argument matchers */ public InvocationInterceptor(ReflectionComparatorMode... modes) { this.modes = modes; } /** * Sets the current wrapped record state. * * @param recordState the state, not null */ public void setRecordState(RecordState recordState) { this.recordState = recordState; } /** * Overriden to report reflection argument matchers for all arguments of the given method invocation. * * @param invocation the method invocation, not null * @return the result of the invocation */ @Override public Object invoke(Invocation invocation) { LastControl.reportLastControl(LenientMocksControl.this); createMatchers(invocation); return recordState.invoke(invocation); } /** * Reports report reflection argument matchers for all arguments of the given method invocation. * An exception will be thrown if there were already matchers reported for the invocation. * * @param invocation the method invocation, not null */ private void createMatchers(Invocation invocation) { List matchers = LastControl.pullMatchers(); if (matchers != null && !matchers.isEmpty()) { if (matchers.size() != invocation.getArguments().length) { throw new IllegalStateException("This mock control does not support mixing of no-argument matchers and per-argument matchers. " + "Either no matchers are defined and the reflection argument matcher is used by default or all matchers are defined explicitly (Eg by using refEq())."); } // put all matchers back since pull removes them for (IArgumentMatcher matcher : matchers) { LastControl.reportMatcher(matcher); } return; } Object[] arguments = invocation.getArguments(); if (arguments == null) { return; } for (Object argument : arguments) { LastControl.reportMatcher(new ReflectionArgumentMatcher(argument, modes)); } } // Pass through delegation @Override public void assertRecordState() { recordState.assertRecordState(); } @Override public void andReturn(Object value) { recordState.andReturn(value); } @Override public void andThrow(Throwable throwable) { recordState.andThrow(throwable); } @Override public void andAnswer(IAnswer answer) { recordState.andAnswer(answer); } @Override public void andStubReturn(Object value) { recordState.andStubReturn(value); } @Override public void andStubThrow(Throwable throwable) { recordState.andStubThrow(throwable); } @Override public void andStubAnswer(IAnswer answer) { recordState.andStubAnswer(answer); } @Override public void asStub() { recordState.asStub(); } @Override public void times(Range range) { recordState.times(range); } @Override public void checkOrder(boolean value) { recordState.checkOrder(value); } @Override public void replay() { recordState.replay(); } @Override public void verify() { recordState.verify(); } /*public void setDefaultReturnValue(Object value) { recordState.setDefaultReturnValue(value); } public void setDefaultThrowable(Throwable throwable) { recordState.setDefaultThrowable(throwable); } public void setDefaultVoidCallable() { recordState.setDefaultVoidCallable(); } public void setDefaultMatcher(ArgumentsMatcher matcher) { recordState.setDefaultMatcher(matcher); } public void setMatcher(Method method, ArgumentsMatcher matcher) { recordState.setMatcher(method, matcher); }*/ /** * @see org.easymock.internal.IMocksControlState#andDelegateTo(java.lang.Object) */ @Override public void andDelegateTo(Object value) { recordState.andDelegateTo(value); } /** * @see org.easymock.internal.IMocksControlState#andStubDelegateTo(java.lang.Object) */ @Override public void andStubDelegateTo(Object value) { recordState.andStubDelegateTo(value); } /** * @see org.easymock.internal.IMocksControlState#checkIsUsedInOneThread(boolean) */ @Override public void checkIsUsedInOneThread(boolean value) { recordState.checkIsUsedInOneThread(value); } /** * @see org.easymock.internal.IMocksControlState#makeThreadSafe(boolean) */ @Override public void makeThreadSafe(boolean value) { recordState.makeThreadSafe(value); } } }