mockit.Invocations Maven / Gradle / Ivy
/*
* JMockit Expectations & Verifications
* Copyright (c) 2006-2010 Rogério Liesenfeld
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package mockit;
import java.util.regex.*;
import mockit.external.hamcrest.*;
import mockit.external.hamcrest.Matcher;
import mockit.external.hamcrest.core.*;
import mockit.external.hamcrest.number.*;
import mockit.internal.expectations.*;
import mockit.internal.startup.*;
import mockit.internal.util.*;
/**
* Provides common user API for both the {@linkplain Expectations record} and {@linkplain Verifications verification}
* phases of a test.
*/
abstract class Invocations
{
static
{
Startup.verifyInitialization();
}
/**
* Matches any {@code Object} reference for the relevant parameter.
* Note that the use of this field will usually require a cast to the specific parameter type.
* If there is any other parameter for which an argument matching constraint can be specified,
* though, the {@code null} reference can be passed instead, as it will also match any
* reference during the replay phase.
*
* Note: in invocations to non-accessible methods or constructors (for example, with
* {@link #invoke(Object, String, Object...)}), use {@link #withAny} instead.
*
* @see #anyInt
*/
protected static final Object any = null;
/**
* Matches any {@code String} value for the relevant parameter.
*
* @see #anyInt
*/
// This is intentional: the empty string causes the compiler to not generate a field read,
// while the null reference is inconvenient with the invoke(...) methods:
protected static final String anyString = new String();
/**
* Matches any {@code long} or {@code Long} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Long anyLong = 0L;
/**
* Matches any {@code int} or {@code Integer} value for the relevant parameter.
*
* When used as argument for a method/constructor invocation in the recording or verification
* phase of a test, specifies the matching of any value passed as argument to
* corresponding invocations in the replay phase.
*
* In the Tutorial
*/
protected static final Integer anyInt = 0;
/**
* Matches any {@code short} or {@code Short} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Short anyShort = 0;
/**
* Matches any {@code byte} or {@code Byte} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Byte anyByte = 0;
/**
* Matches any {@code boolean} or {@code Boolean} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Boolean anyBoolean = false;
/**
* Matches any {@code char} or {@code Character} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Character anyChar = '\0';
/**
* Matches any {@code double} or {@code Double} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Double anyDouble = 0.0;
/**
* Matches any {@code float} or {@code Float} value for the relevant parameter.
*
* @see #anyInt
*/
protected static final Float anyFloat = 0.0F;
/**
* An object assigned to this field will be taken as a handler for each invocation matching the current expectation,
* with the purpose of validating invocation arguments.
* Note that for a recorded expectation such invocations are the ones that will be executed during the
* replay phase, while for a verified expectation they are the ones actually executed during that
* phase.
*
* The object assigned can be of any type, provided its class has a single non-private method
* (therefore, additional methods are allowed and ignored, as long as they are {@code private}).
* This handler method can have any name, as long as its parameters match the ones defined in the mocked
* method or constructor associated with the expectation.
* Corresponding parameters don't need to have the exact same declared type, though, as long as each possible
* invocation argument can be passed to the corresponding parameter in the handler method.
*
* In the case of an expectation recorded for a non-{@code void} method, the handler method is also responsible for
* returning appropriate values to be used by the caller (which normally belongs to the code under test).
* That is, the {@code result} field or the {@code returns(...)} method should not be used together with an
* assignment to this field.
* The same observation applies to the throwing of exceptions/errors from a recorded expectation
* (which can also be done for constructors and {@code void} methods).
*
* When used for an expectation inside a verification block, on the other hand, the handler method should
* normally have a {@code void} return type. Any value eventually returned by the method will be silently ignored in
* this case. Note that a handler method for a verified expectation also shouldn't intentionally throw exceptions or
* errors, since the verified invocation(s) already happened in the replay phase; any exception/error actually thrown
* will simply propagate back to the test method.
*
* Just like with {@linkplain mockit.Delegate delegate classes}, the handler method can declare its first parameter
* as being of type {@link mockit.Invocation}.
*
* In the Tutorial
*/
protected static Object forEachInvocation;
/**
* A non-negative value assigned to this field will be taken as the exact number of times that
* invocations matching the current expectation should occur during replay.
*
* In the Tutorial
*
* @see #minTimes
* @see #maxTimes
*/
protected static int times;
/**
* A non-negative value assigned to this field will be taken as the minimum number of times that
* invocations matching the current expectation should occur during replay.
*
* Both {@code minTimes} and {@code maxTimes} can be specified for the same expectation, as long
* as {@code minTimes} is assigned first.
*
* @see #times
* @see #maxTimes
*/
protected static int minTimes;
/**
* A non-negative value assigned to this field will be taken as the maximum number of times that
* invocations matching the current expectation should occur during replay.
* A negative value implies there is no upper limit.
*
* Both {@code minTimes} and {@code maxTimes} can be specified for the same expectation, as long
* as {@code minTimes} is assigned first.
*
* @see #times
* @see #minTimes
*/
protected static int maxTimes;
/**
* A value assigned to this field will be used as a prefix for the error message to be reported
* if and when the current expectation is violated.
*
* Inside an expectation/verification block, the assignment must follow the
* invocation which records/verifies the expectation; if there is no current expectation at the
* point the assignment appears, an {@code IllegalStateException} is thrown.
*
* Notice there are only two different ways in which an expectation can be violated: either an
* unexpected invocation occurs, or a missing invocation is detected.
*/
protected static CharSequence $;
Invocations() {}
abstract TestOnlyPhase getCurrentPhase();
final Expectation getCurrentExpectation()
{
return getCurrentPhase().getCurrentExpectation();
}
/**
* Specify that the next invocation on the given mocked instance must match a corresponding invocation on the
* same instance in the replay phase.
*
* By default, such instances can be different between the replay phase and the record or verify phase, even though
* the method or constructor invoked is the same, and the invocation arguments all match.
* The use of this method allows invocations to also be matched on the instance invoked.
*
* Typically, tests that need to match instance invocations on the mocked instances invoked will declare two or more
* mock fields and/or parameters of the exact same mocked type. These instances will then be passed to the code under
* test, which will invoke them during the replay phase.
* To avoid the need to explicitly call {@code onInstance(Object)} on each of these different instances of the
* same type, instance matching is implied (and automatically applied to all relevant invocations) whenever
* two or more mocked instances of the same type are in scope for a given test method. This property of the API makes
* the use of {@code onInstance} much less frequent than it might otherwise be.
*
* In most cases, an invocation to the given mocked instance will be made on the value returned by this method (ie,
* a chained invocation).
* However, in the situation where the tested method calls an instance method defined in a mocked super-class
* (possibly an overridden method called through the {@code super} keyword), it will be necessary to match on a
* different instance than the one used for recording invocations.
* To do so, this method should be given the desired instance to match, while the invocation to be recorded should be
* done on the available mocked instance, which must be a different one (otherwise a non-mocked method would get
* executed).
* This is valid only if the instance to be matched is assignable to the mocked type, and typically occurs when
* partially mocking a class hierarchy.
*
* In the Tutorial
*
* @return the given mocked instance, allowing the invocation to be recorded/verified to immediately follow the call
* to this method
*/
protected final T onInstance(T mockedInstance)
{
if (mockedInstance == null) {
throw new NullPointerException("Missing mocked instance to match");
}
getCurrentPhase().setNextInstanceToMatch(mockedInstance);
return mockedInstance;
}
// Methods for argument matching ///////////////////////////////////////////////////////////////////////////////////
/**
* Adds a custom argument matcher for a parameter in the current invocation.
*
* The given matcher can be any existing Hamcrest matcher or a user provided
* one.
* Additionally, it can be an instance of an arbitrary invocation handler class, similar
* to those used with the {@link #forEachInvocation} field.
* In this case, the non-{@code private} handler method must have a single parameter of
* a type capable of receiving the relevant argument values.
* The name of this handler method does not matter. Its return type, on the other hand, should
* either be {@code boolean} or {@code void}. In the first case, a return value of
* {@code true} will indicate a successful match for the actual invocation argument at replay
* time, while a return of {@code false} will cause the test to fail. In the second case, instead
* of returning a value the invocation handler method should validate the actual invocation
* argument through an {@code assert} statement or a JUnit/TestNG assertion.
*
* For additional details, refer to {@link #withEqual(Object)}.
*
* @param argValue an arbitrary value of the proper type, necessary to provide a valid argument
* to the invocation parameter
* @param argumentMatcher an instance of a class implementing the {@code org.hamcrest.Matcher}
* interface, or any other instance with an appropriate invocation handler method
*
* @return the given {@code argValue}
*/
protected final T with(T argValue, Object argumentMatcher)
{
addMatcher(HamcrestAdapter.create(argumentMatcher));
return argValue;
}
/**
* Adds a custom argument matcher for a parameter in the current invocation.
* This works like {@link #with(Object, Object)}, but attempting to extract the argument value
* from the supplied argument matcher.
*
* @param argumentMatcher an instance of a class implementing the {@code org.hamcrest.Matcher}
* interface, or any other instance with an appropriate invocation handler method
*
* @return the value recorded inside the given argument matcher, or {@code null} if no such value
* could be determined
*/
protected final T with(Object argumentMatcher)
{
HamcrestAdapter adapter = HamcrestAdapter.create(argumentMatcher);
addMatcher(adapter);
Object argValue = adapter.getInnerValue();
//noinspection unchecked
return (T) argValue;
}
private void addMatcher(Matcher> matcher)
{
getCurrentPhase().addArgMatcher(matcher);
}
/**
* Same as {@link #withEqual(Object)}, but matching any argument value of the appropriate type.
*
* Consider using instead the "anyXyz" field appropriate to the parameter type:
* {@link #anyBoolean}, {@link #anyByte}, {@link #anyChar}, {@link #anyDouble},
* {@link #anyFloat}, {@link #anyInt}, {@link #anyLong}, {@link #anyShort}, {@link #anyString},
* or {@link #any} for other reference types.
*
* Note: when using {@link #invoke(Object, String, Object...)}, etc., it's valid to pass
* {@code withAny(ParameterType.class)} if an actual instance of the parameter type cannot be
* created.
*
* @param arg an arbitrary value which will match any argument value in the replay phase
*
* @return the input argument
*/
protected final T withAny(T arg)
{
addMatcher(new IsAnything());
return arg;
}
/**
* When called as argument for a method/constructor invocation in the recording or verification
* phase of a test, creates a new matcher that will check if the given value is
* {@link Object#equals(Object) equal} to the corresponding invocation argument in the replay
* phase.
*
* The matcher is added to the end of the list of argument matchers for the invocation being
* recorded/verified. It cannot be reused for a different parameter.
*
* In the Tutorial
*
* @param arg the expected argument value
*
* @return the input argument
*/
protected final T withEqual(T arg)
{
//noinspection unchecked
addMatcher(new IsEqual(arg));
return arg;
}
/**
* Same as {@link #withEqual(Object)}, but checking that a numeric invocation argument in the
* replay phase is sufficiently close to the given value.
*/
protected final double withEqual(double value, double delta)
{
addMatcher(new IsCloseTo(value, delta));
return value;
}
/**
* Same as {@link #withEqual(Object)}, but checking that a numeric invocation argument in the
* replay phase is sufficiently close to the given value.
*/
protected final float withEqual(float value, double delta)
{
addMatcher(new IsCloseTo(value, delta));
return value;
}
/**
* Same as {@link #withEqual(Object)}, but checking that an invocation argument in the replay
* phase is an instance of the same class as the given object.
*
* Equivalent to a call withInstanceOf(arg.getClass())
, except that it returns
* {@code arg} instead of {@code null}.
*/
protected final T withInstanceLike(T object)
{
addMatcher(new IsInstanceOf(object.getClass()));
return object;
}
/**
* Same as {@link #withEqual(Object)}, but checking that an invocation argument in the replay
* phase is an instance of the given class.
*
* @return always null; if you need a specific return value, use
* {@link #withInstanceLike(Object)}
*/
protected final T withInstanceOf(Class argClass)
{
addMatcher(new IsInstanceOf(argClass));
return null;
}
/**
* Same as {@link #withEqual(Object)}, but checking that the invocation argument in the replay
* phase is different from the given value.
*/
protected final T withNotEqual(T arg)
{
//noinspection unchecked
addMatcher(new IsNot(new IsEqual(arg)));
return arg;
}
/**
* Same as {@link #withEqual(Object)}, but checking that an invocation argument in the replay
* phase is not {@code null}.
*
* @return always {@code null}
*/
protected final T withNotNull()
{
addMatcher(new IsNot(new IsNull()));
return null;
}
/**
* Same as {@link #withEqual(Object)}, but checking that an invocation argument in the replay
* phase is {@code null}.
*
* @return always {@code null}
*/
protected final T withNull()
{
addMatcher(new IsNull());
return null;
}
/**
* Same as {@link #withEqual(Object)}, but checking that an invocation argument in the replay
* phase is the exact same instance as the one in the recorded/verified invocation.
*/
protected final T withSameInstance(T object)
{
//noinspection unchecked
addMatcher(new IsSame(object));
return object;
}
// Text-related matchers ///////////////////////////////////////////////////////////////////////////////////////////
/**
* Same as {@link #withEqual(Object)}, but checking that a textual invocation argument in the
* replay phase contains the given text as a substring.
*/
protected final T withSubstring(T text)
{
addMatcher(new StringContains(text));
return text;
}
/**
* Same as {@link #withEqual(Object)}, but checking that a textual invocation argument in the
* replay phase starts with the given text.
*/
protected final T withPrefix(T text)
{
addMatcher(new StringStartsWith(text));
return text;
}
/**
* Same as {@link #withEqual(Object)}, but checking that a textual invocation argument in the
* replay phase ends with the given text.
*/
protected final T withSuffix(T text)
{
addMatcher(new StringEndsWith(text));
return text;
}
/**
* Same as {@link #withEqual(Object)}, but checking that a textual invocation argument in the
* replay phase matches the given {@link Pattern regular expression}.
*
* Note that this can be used for any string comparison, including case insensitive ones (with
* {@code "(?i)"} in the regex).
*
* @see Pattern#compile(String, int)
*/
protected final T withMatch(T regex)
{
final Pattern pattern = Pattern.compile(regex.toString());
addMatcher(new BaseMatcher()
{
public boolean matches(Object item)
{
return pattern.matcher((CharSequence) item).matches();
}
public void describeTo(Description description)
{
description.appendText("a string matching ").appendValue(pattern);
}
});
return regex;
}
// Methods for instantiating non-accessible classes ////////////////////////////////////////////////////////////////
/**
* Specifies an expected constructor invocation for a given class.
*
* This is useful for invoking non-accessible constructors (private ones, for example) from the
* test, which otherwise could not be called normally.
*
* In the Tutorial
*
* @param className the fully qualified name of the desired class (which should not be accessible
* to the test; otherwise just refer to it in code)
* @param parameterTypes the formal parameter types for the desired constructor, possibly empty
* @param initArgs the invocation arguments for the constructor, which must be consistent with
* the specified parameter types
* @param interface or super-class type to which the returned instance should be assignable
*
* @return a newly created instance of the specified class, initialized with the specified
* constructor and arguments
*
* @see #newInstance(String, Object...)
* @see #newInnerInstance(String, Object, Object...)
*/
protected final T newInstance(String className, Class>[] parameterTypes, Object... initArgs)
{
//noinspection unchecked
return (T) Utilities.newInstance(className, parameterTypes, initArgs);
}
/**
* The same as {@link #newInstance(String, Class[], Object...)}, but for the case where each
* initialization argument is known to be null or non-null at the call point.
*
* However, {@code null} argument values cannot directly be passed to this method;
* if the parameter value must be {@code null}, then the corresponding {@code Class} literal must
* be passed instead.
*
* @throws IllegalArgumentException if one of the given arguments is {@code null}
*/
protected final T newInstance(String className, Object... nonNullInitArgs)
{
//noinspection unchecked
return (T) Utilities.newInstance(className, nonNullInitArgs);
}
/**
* The same as {@link #newInstance(String, Class[], Object...)}, but for instantiating an inner
* non-accessible class of some other class, and where all other (if any) initialization
* arguments are known to be non null.
*
* @param innerClassSimpleName simple name of the inner class, that is, the part after the "$"
* character in its full name
* @param outerClassInstance the outer class instance to which the inner class instance will
* belong
*/
protected final T newInnerInstance(
String innerClassSimpleName, Object outerClassInstance, Object... nonNullInitArgs)
{
//noinspection unchecked
return (T) Utilities.newInnerInstance(innerClassSimpleName, outerClassInstance, nonNullInitArgs);
}
// Methods for invoking non-accessible methods on instances or classes /////////////////////////////////////////////
/**
* Specifies an expected invocation to a given instance method, with a given list of arguments.
*
* This is useful when the next expected method is not accessible (private, for example) from the test, and
* therefore can not be called normally. It should not be used for calling accessible methods.
*
* Additionally, this can also be used to directly test private methods, when there is no other way to do so, or it
* would be too difficult by indirect means. Note that in such a case the target instance will actually be a "real"
* (non-mocked) object, not a mocked instance.
*
* In the Tutorial
*
* @param fieldOwner the instance on which the invocation is to be done; must not be null
* @param methodName the name of the expected method
* @param methodArgs zero or more non-null expected parameter values for the invocation; if a
* null value needs to be passed, the Class object for the parameter type must be passed instead
*
* @return the return value from the invoked method, wrapped if primitive
*
* @see #invoke(Class, String, Object...)
*/
protected final T invoke(Object fieldOwner, String methodName, Object... methodArgs)
{
//noinspection unchecked
return (T) Utilities.invoke(fieldOwner.getClass(), fieldOwner, methodName, methodArgs);
}
/**
* Specifies an expected invocation to a given static method, with a given list of arguments.
*
* This is useful when the next expected method is not accessible (private, for example) from the
* test, and therefore cannot be called normally. It should not be used for
* calling accessible methods.
*
* Additionally, this can also be used to directly test private methods, when there is no other
* way to do so, or it would be too difficult by indirect means. Note that in such a case the
* target class will normally be a "real", non-mocked, class in the code under test.
*
* @param methodOwner the class on which the invocation is to be done; must not be null
* @param methodName the name of the expected static method
* @param methodArgs zero or more non-null expected parameter values for the invocation; if a
* null value needs to be passed, the Class object for the parameter type must be passed instead
*/
protected final T invoke(Class> methodOwner, String methodName, Object... methodArgs)
{
//noinspection unchecked
return (T) Utilities.invoke(methodOwner, null, methodName, methodArgs);
}
// Methods for getting/setting non-accessible fields on instances or classes ///////////////////////////////////////
/**
* Gets the value of a non-accessible field from a given object.
*
* In the Tutorial
*
* @param fieldOwner the instance from which to get the field value
* @param fieldName the name of the field to get
*
* @see #setField(Object, String, Object)
*/
protected final T getField(Object fieldOwner, String fieldName)
{
//noinspection unchecked
return (T) Utilities.getField(fieldOwner.getClass(), fieldName, fieldOwner);
}
/**
* Gets the value of a non-accessible field from a given object, assuming there is only
* one field declared in the class of the given object whose type can receive values of the
* specified field type.
*
* @param fieldOwner the instance from which to get the field value
* @param fieldType the declared type of the field, or a sub-type of the declared field type
*
* @see #getField(Object, String)
*
* @throws IllegalArgumentException if either the desired field is not found, or more than one is
*/
protected final T getField(Object fieldOwner, Class fieldType)
{
//noinspection unchecked
return Utilities.getField(fieldOwner.getClass(), fieldType, fieldOwner);
}
/**
* Gets the value of a non-accessible static field defined in a given class.
*
* @param fieldOwner the class from which to get the field value
* @param fieldName the name of the static field to get
*
* @see #setField(Class, String, Object)
*/
protected final T getField(Class> fieldOwner, String fieldName)
{
//noinspection unchecked
return (T) Utilities.getField(fieldOwner, fieldName, null);
}
/**
* Gets the value of a non-accessible static field defined in a given class.
*
* @param fieldOwner the class from which to get the field value
* @param fieldType the declared type of the field, or a sub-type of the declared field type
*
* @see #setField(Class, String, Object)
*/
protected final T getField(Class> fieldOwner, Class fieldType)
{
//noinspection unchecked
return Utilities.getField(fieldOwner, fieldType, null);
}
/**
* Sets the value of a non-accessible field on a given object.
*
* In the Tutorial
*
* @param fieldOwner the instance on which to set the field value
* @param fieldName the name of the field to set
* @param fieldValue the value to set the field to
*
* @see #setField(Class, String, Object)
*/
protected final void setField(Object fieldOwner, String fieldName, Object fieldValue)
{
Utilities.setField(fieldOwner.getClass(), fieldOwner, fieldName, fieldValue);
}
/**
* Same as {@link #setField(Object, String, Object)}, except that the field is looked up by the
* type of the given field value instead of by name.
*
* @throws IllegalArgumentException if no field or more than one is found in the target class to
* which the given value can be assigned
*/
protected final void setField(Object fieldOwner, Object fieldValue)
{
Utilities.setField(fieldOwner.getClass(), fieldOwner, null, fieldValue);
}
/**
* Sets the value of a non-accessible static field on a given class.
*
* @param fieldOwner the class on which the static field is defined
* @param fieldName the name of the field to set
* @param fieldValue the value to set the field to
*/
protected final void setField(Class> fieldOwner, String fieldName, Object fieldValue)
{
Utilities.setField(fieldOwner, null, fieldName, fieldValue);
}
/**
* Same as {@link #setField(Class, String, Object)}, except that the field is looked up by the
* type of the given field value instead of by name.
*
* @param fieldOwner the class on which the static field is defined
* @param fieldValue the value to set the field to
*/
protected final void setField(Class> fieldOwner, Object fieldValue)
{
Utilities.setField(fieldOwner, null, null, fieldValue);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy