mockit.Expectations Maven / Gradle / Ivy
Show all versions of jmockit Show documentation
/*
* Copyright (c) 2006 JMockit developers
* This file is subject to the terms of the MIT license (see LICENSE.txt).
*/
package mockit;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import mockit.internal.expectations.RecordAndReplayExecution;
import mockit.internal.expectations.RecordPhase;
/**
* 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, and which need to exhibit some specific behavior for the purposes of
* the test. When a match is detected, the recorded {@linkplain #result result} (if any) 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:
*
*
{@code
* // 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 {
/**
* 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.
*
* @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() {
RecordAndReplayExecution execution = new RecordAndReplayExecution(this, (Object[]) null);
// noinspection ConstantConditions
currentPhase = execution.getRecordPhase();
}
/**
* Same as {@link #Expectations()}, except that one or more objects will be partially mocked according to the
* expectations recorded in the expectation block.
*
* During replay, any invocations to instance methods on these objects will execute real production code, unless a
* matching expectation was recorded.
*
* @param objectsToBePartiallyMocked
* one or more objects to be partially mocked
*
* @throws IllegalArgumentException
* if given a Class
object, or if given a value/instance of an interface, an annotation, an
* array, a primitive/wrapper type, a synthetic class, or a
* {@linkplain java.lang.reflect.Proxy#isProxyClass(Class) proxy class}
*
* @see Tutorial
*/
protected Expectations(@NonNull Object... objectsToBePartiallyMocked) {
RecordAndReplayExecution execution = new RecordAndReplayExecution(this, objectsToBePartiallyMocked);
// noinspection ConstantConditions
currentPhase = 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) {
int n = remainingValues.length;
Object[] values = new Object[2 + n];
values[0] = firstValue;
values[1] = secondValue;
System.arraycopy(remainingValues, 0, values, 2, n);
((RecordPhase) currentPhase).addSequenceOfReturnValues(values);
}
}