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