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 }