1 /*
2 * MIT License
3 * Copyright (c) 2006-2025 JMockit developers
4 * See LICENSE file for full license text.
5 */
6 package mockit;
7
8 import edu.umd.cs.findbugs.annotations.NonNull;
9 import edu.umd.cs.findbugs.annotations.Nullable;
10
11 import java.util.Collections;
12 import java.util.Iterator;
13 import java.util.List;
14 import java.util.Optional;
15
16 import mockit.internal.expectations.RecordAndReplayExecution;
17 import mockit.internal.expectations.RecordPhase;
18
19 /**
20 * Used to <em>record</em> expectations on {@linkplain Mocked mocked} types and their instances.
21 * <p>
22 * Each recorded expectation is intended to match one or more method or constructor invocations, that we expect will
23 * occur during the execution of code under test, and which need to exhibit some specific behavior for the purposes of
24 * the test. When a match is detected, the recorded {@linkplain #result result} (if any) is returned to the caller.
25 * Alternatively, a recorded exception/error is thrown, or an arbitrary {@linkplain Delegate delegate} method is
26 * executed.
27 * <p>
28 * Expectations are recorded simply by invoking the desired method or constructor on the mocked type/instance, during
29 * the initialization of an <code>Expectations</code> object. This is done by instantiating an anonymous subclass
30 * containing an instance initialization body, or as we call it, an <em>expectation block</em>:
31 *
32 * <pre>{@code
33 * // <em>Record</em> one or more expectations on available mocked types/instances.
34 * new Expectations() {{
35 * <strong>mock1</strong>.expectedMethod(anyInt); result = 123; times = 2;
36 * <strong>mock2</strong>.anotherExpectedMethod(1, "test"); result = "Abc";
37 * }};
38 *
39 * // Exercise tested code, with previously recorded expectations now available for <em>replay</em>.
40 * codeUnderTest.doSomething();
41 * }</pre>
42 *
43 * During replay, invocations matching a recorded expectation must occur at least <em>once</em> (unless specified
44 * otherwise); if, by the end of the test, no matching invocation occurred for a given recorded expectation, the test
45 * will fail with a <code>MissingInvocation</code> error.
46 * <p>
47 * When multiple expectations are recorded, matching invocations are allowed to occur in a <em>different</em> order. So,
48 * the order in which expectations are recorded is not significant.
49 * <p>
50 * Besides the special {@link #result} field already mentioned, there are several other fields and methods which can be
51 * used inside the expectation block: a) {@link #returns(Object, Object, Object...)}, a convenience method for returning
52 * a <em>sequence</em> of values; b) argument matchers such as {@link #anyInt}, {@link #anyString},
53 * {@link #withNotNull()}, etc., which relax or constrain the matching of argument values; c) the {@link #times},
54 * {@link #minTimes}, and {@link #maxTimes} fields, which relax or constrain the expected and/or allowed number of
55 * matching invocations.
56 * <p>
57 * By default, the exact instance on which instance method invocations will occur during replay is <em>not</em> verified
58 * to be the same as the instance used when recording the expectation. That said, instance-specific matching can be
59 * obtained by declaring the mocked type as {@linkplain Injectable @Injectable}, or by declaring multiple mock fields
60 * and/or mock parameters of the same mocked type (so that separate expectations can be recorded for each mock
61 * instance).
62 * <p>
63 * Invocations occurring during replay, whether they matched recorded expectations or not, can be explicitly verified
64 * <em>after</em> exercising the code under test. To that end, we use a set of complementary base classes:
65 * {@link Verifications}, {@link VerificationsInOrder}, and {@link FullVerifications}. Similar to expectation blocks,
66 * these classes allow us to create <em>verification</em> blocks.
67 *
68 * @see #Expectations()
69 * @see #Expectations(Object...)
70 * @see <a href="http://jmockit.github.io/tutorial/Mocking.html#expectation" target="tutorial">Tutorial</a>
71 */
72 public class Expectations extends Invocations {
73 /**
74 * A value assigned to this field will be taken as the result for the expectation that is being recorded.
75 * <p>
76 * If the value is a {@link Throwable} then it will be <em>thrown</em> when a matching invocation later occurs.
77 * Otherwise, it's assumed to be a <em>return value</em> for a non-<code>void</code> method, and will be returned
78 * from a matching invocation.
79 * <p>
80 * If no result is recorded for a given expectation, then all matching invocations will return the appropriate
81 * default value according to the method return type:
82 * <ul>
83 * <li>Most <code>java.lang</code> types (<code>String</code>, <code>Object</code>, etc.): returns
84 * <code>null</code>.</li>
85 * <li><code>java.math</code> types (<code>BigDecimal</code>, etc.): returns <code>null</code>.</li>
86 * <li>Primitive/wrapper types: returns the standard default value (<code>false</code> for
87 * <code>boolean/Boolean</code>, <code>0</code> for <code>int/Integer</code>, and so on).</li>
88 * <li><code>java.util.List</code>, <code>java.util.Collection</code>, or <code>java.lang.Iterable</code>: returns
89 * {@link Collections#EMPTY_LIST}.</li>
90 * <li><code>java.util.Iterator</code> or <code>java.util.ListIterator</code>: returns an empty iterator.</li>
91 * <li><code>java.util.Set</code>: returns {@link Collections#EMPTY_SET}.</li>
92 * <li><code>java.util.SortedSet</code>: returns an unmodifiable empty sorted set.</li>
93 * <li><code>java.util.Map</code>: returns {@link Collections#EMPTY_MAP}.</li>
94 * <li><code>java.util.SortedMap</code>: returns an unmodifiable empty sorted map.</li>
95 * <li><code>java.util.Optional</code>: returns {@link Optional#empty()}.</li>
96 * <li>Other reference types: returns a mocked instance through cascading.</li>
97 * <li>Array types: returns an array with zero elements (empty) in each dimension.</li>
98 * </ul>
99 * When an expectation is recorded for a method which actually <em>returns</em> an exception or error (as opposed to
100 * <em>throwing</em> one), then the {@link #returns(Object, Object, Object...)} method should be used instead, as it
101 * only applies to return values.
102 * <p>
103 * Assigning a value whose type differs from the method return type will cause an
104 * <code>IllegalArgumentException</code> to be thrown, unless it can be safely converted to the return type. One
105 * such conversion is from an array to a collection or iterator. Another is from an array of at least two dimensions
106 * to a map, with the first dimension providing the keys and the second the values. Yet another conversion is from a
107 * single value to a container type holding that value.
108 * <p>
109 * A sequence of <em>consecutive results</em> can be recorded simply by assigning the field multiple times for the
110 * same expectation. Alternatively, the desired sequence of results for a single-valued return type can be recorded
111 * by assigning an array, an {@link Iterable}, or an {@link Iterator} containing the individual results in order.
112 * <p>
113 * Results that depend on some programming logic can be provided through a {@linkplain Delegate} object assigned to
114 * the field. This applies to <code>void</code> and non-<code>void</code> methods, as well as to constructors.
115 *
116 * @see <a href="http://jmockit.github.io/tutorial/Mocking.html#results" target="tutorial">Tutorial</a>
117 */
118 @Nullable
119 protected Object result;
120
121 /**
122 * Registers one or more expectations recorded on available mocked types and/or mocked instances, as written inside
123 * the instance initialization body of an anonymous subclass.
124 *
125 * @see #Expectations(Object...)
126 * @see <a href="http://jmockit.github.io/tutorial/Mocking.html#expectation" target="tutorial">Tutorial</a>
127 */
128 protected Expectations() {
129 RecordAndReplayExecution execution = new RecordAndReplayExecution(this, (Object[]) null);
130 // noinspection ConstantConditions
131 currentPhase = execution.getRecordPhase();
132 }
133
134 /**
135 * Same as {@link #Expectations()}, except that one or more objects will be partially mocked according to the
136 * expectations recorded in the expectation block.
137 * <p>
138 * During replay, any invocations to instance methods on these objects will execute real production code, unless a
139 * matching expectation was recorded.
140 *
141 * @param objectsToBePartiallyMocked
142 * one or more objects to be partially mocked
143 *
144 * @throws IllegalArgumentException
145 * if given a <code>Class</code> object, or if given a value/instance of an interface, an annotation, an
146 * array, a primitive/wrapper type, a synthetic class, or a
147 * {@linkplain java.lang.reflect.Proxy#isProxyClass(Class) proxy class}
148 *
149 * @see <a href="http://jmockit.github.io/tutorial/Mocking.html#partial" target="tutorial">Tutorial</a>
150 */
151 protected Expectations(@NonNull Object... objectsToBePartiallyMocked) {
152 RecordAndReplayExecution execution = new RecordAndReplayExecution(this, objectsToBePartiallyMocked);
153 // noinspection ConstantConditions
154 currentPhase = execution.getRecordPhase();
155 }
156
157 /**
158 * Specifies that the previously recorded method invocation will return a given sequence of values during replay.
159 * <p>
160 * Calling this method is equivalent to assigning the {@link #result} field two or more times in sequence, or
161 * assigning it a single time with an array or iterable containing the same sequence of values.
162 * <p>
163 * Certain data conversions will be applied, depending on the return type of the recorded method:
164 * <ol>
165 * <li>If the return type is iterable and can receive a {@link List} value, then the given sequence of values will
166 * be converted into an <code>ArrayList</code>; this list will then be returned by matching invocations at replay
167 * time.</li>
168 * <li>If the return type is <code>SortedSet</code> or a sub-type, then the given sequence of values will be
169 * converted into a <code>TreeSet</code>; otherwise, if it is <code>Set</code> or a sub-type, then a
170 * <code>LinkedHashSet</code> will be created to hold the values; the set will then be returned by matching
171 * invocations at replay time.</li>
172 * <li>If the return type is <code>Iterator</code> or a sub-type, then the given sequence of values will be
173 * converted into a <code>List</code> and the iterator created from this list will be returned by matching
174 * invocations at replay time.</li>
175 * <li>If the return type is an array, then the given sequence of values will be converted to an array of the same
176 * type, which will be returned by matching invocations at replay time.</li>
177 * </ol>
178 * The current expectation will have its upper invocation count automatically set to the total number of values
179 * specified to be returned. This upper limit can be overridden through the <code>maxTimes</code> field, if
180 * necessary.
181 *
182 * @param firstValue
183 * the first value to be returned at replay time
184 * @param secondValue
185 * the second value to be returned at replay time
186 * @param remainingValues
187 * any remaining values to be returned, in the same order
188 *
189 * @see <a href="http://jmockit.github.io/tutorial/Mocking.html#results" target="tutorial">Tutorial</a>
190 */
191 protected final void returns(@Nullable Object firstValue, @Nullable Object secondValue,
192 @NonNull Object... remainingValues) {
193 int n = remainingValues.length;
194 Object[] values = new Object[2 + n];
195 values[0] = firstValue;
196 values[1] = secondValue;
197 System.arraycopy(remainingValues, 0, values, 2, n);
198
199 ((RecordPhase) currentPhase).addSequenceOfReturnValues(values);
200 }
201 }