mockit.Expectations Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmockit Show documentation
Show all versions of jmockit Show documentation
JMockit is a Java toolkit for automated developer testing.
It contains mocking/faking APIs and a code coverage tool, supporting both JUnit and TestNG.
The mocking APIs allow all kinds of Java code, without testability restrictions, to be tested
in isolation from selected dependencies.
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import java.util.*;
import javax.annotation.*;
import mockit.internal.expectations.*;
/**
* Used to record expectations on {@linkplain Mocked mocked} types and their instances.
*
* Each recorded expectation is intended to match one or more method or constructor invocations, that we expect will occur during the execution of
* code under test.
* When a match is detected, the recorded {@linkplain #result result} is returned to the caller.
* Alternatively, a recorded exception/error is thrown, or an arbitrary {@linkplain Delegate delegate} method is executed.
*
* Expectations are recorded simply by invoking the desired method or constructor on the mocked type/instance, during the initialization of an
* Expectations object.
* This is done by instantiating an anonymous subclass containing an instance initialization body, or as we call it, an expectation block:
*
* // Record one or more expectations on available mocked types/instances.
* new Expectations() {{
* mock1.expectedMethod(anyInt); result = 123; times = 2;
* mock2.anotherExpectedMethod(1, "test"); result = "Abc";
* }};
*
* // Exercise tested code, with previously recorded expectations now available for replay.
* codeUnderTest.doSomething();
*
* During replay, invocations matching a recorded expectation must occur at least once (unless specified otherwise);
* if, by the end of the test, no matching invocation occurred for a given recorded expectation, the test will fail with a
* MissingInvocation error.
*
* When multiple expectations are recorded, matching invocations are allowed to occur in a different order.
* So, the order in which expectations are recorded is not significant.
*
* Besides the special {@link #result} field already mentioned, there are several other fields and methods which can be used inside the
* expectation block:
* a) {@link #returns(Object, Object, Object...)}, a convenience method for returning a sequence of values;
* b) argument matchers such as {@link #anyInt}, {@link #anyString}, {@link #withNotNull()}, etc., which relax or constrain the matching of
* argument values;
* c) the {@link #times}, {@link #minTimes}, and {@link #maxTimes} fields, which relax or constrain the expected and/or allowed number of matching
* invocations.
*
* By default, the exact instance on which instance method invocations will occur during replay is not verified to be the same as the
* instance used when recording the expectation.
* That said, instance-specific matching can be obtained by declaring the mocked type as {@linkplain Injectable @Injectable}, or by declaring
* multiple mock fields and/or mock parameters of the same mocked type (so that separate expectations can be recorded for each mock instance).
*
* Invocations occurring during replay, whether they matched recorded expectations or not, can be explicitly verified after exercising the
* code under test.
* To that end, we use a set of complementary base classes: {@link Verifications}, {@link VerificationsInOrder}, and {@link FullVerifications}.
* Similar to expectation blocks, these classes allow us to create verification blocks.
*
* @see #Expectations()
* @see #Expectations(Object...)
* @see Tutorial
*/
public class Expectations extends Invocations
{
@Nonnull private final RecordAndReplayExecution execution;
/**
* A value assigned to this field will be taken as the result for the expectation that is being recorded.
*
* If the value is a {@link Throwable} then it will be thrown when a matching invocation later occurs.
* Otherwise, it's assumed to be a return value for a non-void
method, and will be returned from a matching
* invocation.
*
* If no result is recorded for a given expectation, then all matching invocations will return the appropriate default value according
* to the method return type:
*
* - Most java.lang types (String, Object, etc.): returns null.
* - java.math types (BigDecimal, etc.): returns null.
* - Primitive/wrapper types: returns the standard default value (
false
for boolean/Boolean, 0 for
* int/Integer, and so on).
*
* - java.util.List, java.util.Collection, or java.lang.Iterable: returns
* {@link Collections#EMPTY_LIST}.
* - java.util.Iterator or java.util.ListIterator: returns an empty iterator.
* - java.util.Set: returns {@link Collections#EMPTY_SET}.
* - java.util.SortedSet: returns an unmodifiable empty sorted set.
* - java.util.Map: returns {@link Collections#EMPTY_MAP}.
* - java.util.SortedMap: returns an unmodifiable empty sorted map.
* - java.util.Optional: returns {@link Optional#empty()}.
* - Other reference types: returns a mocked instance through cascading.
* - Array types: returns an array with zero elements (empty) in each dimension.
*
*
* When an expectation is recorded for a method which actually returns an exception or error (as opposed to throwing
* one), then the {@link #returns(Object, Object, Object...)} method should be used instead, as it only applies to return values.
*
* Assigning a value whose type differs from the method return type will cause an IllegalArgumentException to be thrown, unless
* it can be safely converted to the return type.
* One such conversion is from an array to a collection or iterator.
* Another is from an array of at least two dimensions to a map, with the first dimension providing the keys and the second the values.
* Yet another conversion is from a single value to a container type holding that value.
*
* A sequence of consecutive results can be recorded simply by assigning the field multiple times for the same expectation.
* Alternatively, the desired sequence of results for a single-valued return type can be recorded by assigning an array, an
* {@link Iterable}, or an {@link Iterator} containing the individual results in order.
*
* Results that depend on some programming logic can be provided through a {@linkplain Delegate} object assigned to the field.
* This applies to void and non-void
methods, as well as to constructors.
*
* Finally, when recording an expectation on a constructor of a mocked class, an arbitrary instance of said class can be
* assigned to the field.
* In this case, additional expectations recorded or verified on the assigned instance will also match invocations to instance methods
* made on future instances, provided they get created through a matching constructor invocation.
*
* @see #returns(Object, Object, Object...)
* @see Tutorial
*/
@Nullable
protected Object result;
/**
* Registers one or more expectations recorded on available mocked types and/or mocked instances, as written inside the instance
* initialization body of an anonymous subclass.
*
* @see #Expectations(Object...)
* @see Tutorial
*/
protected Expectations() {
execution = new RecordAndReplayExecution(this, (Object[]) null);
}
/**
* Same as {@link #Expectations()}, except that one or more classes will be partially mocked according to the expectations recorded in the
* expectation block.
*
* The classes to be partially mocked are those directly specified through their Class objects as well as those to which any given
* objects belong.
* During replay, any invocations to one of these classes or objects will execute real production code, unless a matching expectation was
* recorded.
* This mechanism, however, does not apply to native methods, which are not supported for partial mocking.
*
* For a given Class object, all constructors and methods can be mocked, from the specified class up to but not including
* java.lang.Object.
* For a given object, only methods can be mocked, not constructors; also, during replay, invocations to instance methods will only
* match expectations recorded on the given instance (or instances, if more than one was given).
*
* @param classesOrObjectsToBePartiallyMocked one or more classes or objects whose classes are to be partially mocked
*
* @throws IllegalArgumentException if given a Class object for an interface, an annotation, an array, a primitive/wrapper type, a
* synthetic class, a {@linkplain java.lang.reflect.Proxy#isProxyClass(Class) proxy class}, or if given a value/instance of such a type
*
* @see Tutorial
*/
protected Expectations(@Nonnull Object... classesOrObjectsToBePartiallyMocked) {
execution = new RecordAndReplayExecution(this, classesOrObjectsToBePartiallyMocked);
}
@Nullable @Override
final RecordPhase getCurrentPhase() { return execution.getRecordPhase(); }
/**
* Specifies that the previously recorded method invocation will return a given sequence of values during replay.
*
* Calling this method is equivalent to assigning the {@link #result} field two or more times in sequence, or assigning it a single time
* with an array or iterable containing the same sequence of values.
*
* Certain data conversions will be applied, depending on the return type of the recorded method:
*
* - If the return type is iterable and can receive a {@link List} value, then the given sequence of values will be converted into an
* ArrayList; this list will then be returned by matching invocations at replay time.
* - If the return type is SortedSet or a sub-type, then the given sequence of values will be converted into a
* TreeSet; otherwise, if it is Set or a sub-type, then a LinkedHashSet will be created to hold the values;
* the set will then be returned by matching invocations at replay time.
* - If the return type is Iterator or a sub-type, then the given sequence of values will be converted into a List
* and the iterator created from this list will be returned by matching invocations at replay time.
* - If the return type is an array, then the given sequence of values will be converted to an array of the same type, which will be
* returned by matching invocations at replay time.
*
* The current expectation will have its upper invocation count automatically set to the total number of values specified to be returned.
* This upper limit can be overridden through the maxTimes field, if necessary.
*
* @param firstValue the first value to be returned at replay time
* @param secondValue the second value to be returned at replay time
* @param remainingValues any remaining values to be returned, in the same order
*
* @see Tutorial
*/
protected final void returns(@Nullable Object firstValue, @Nullable Object secondValue, @Nonnull Object... remainingValues) {
RecordPhase currentPhase = getCurrentPhase();
if (currentPhase != null) {
int n = remainingValues.length;
Object[] values = new Object[2 + n];
values[0] = firstValue;
values[1] = secondValue;
System.arraycopy(remainingValues, 0, values, 2, n);
currentPhase.addSequenceOfReturnValues(values);
}
}
}