View Javadoc
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 static org.junit.jupiter.api.Assertions.assertEquals;
9   import static org.junit.jupiter.api.Assertions.assertNotNull;
10  import static org.junit.jupiter.api.Assertions.assertNull;
11  import static org.junit.jupiter.api.Assertions.assertThrows;
12  import static org.junit.jupiter.api.Assertions.assertTrue;
13  import static org.junit.jupiter.api.Assertions.fail;
14  
15  import java.io.File;
16  import java.io.FileNotFoundException;
17  import java.nio.file.Path;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import org.junit.jupiter.api.Test;
22  
23  /**
24   * The Class FakeInvocationProceedTest.
25   */
26  class FakeInvocationProceedTest {
27  
28      /**
29       * The Class BaseClassToBeFaked.
30       */
31      public static class BaseClassToBeFaked {
32  
33          /** The name. */
34          protected String name;
35  
36          /**
37           * Base method.
38           *
39           * @param i
40           *            the i
41           *
42           * @return the int
43           */
44          public final int baseMethod(int i) {
45              return i + 1;
46          }
47  
48          /**
49           * Method to be faked.
50           *
51           * @param i
52           *            the i
53           *
54           * @return the int
55           */
56          protected int methodToBeFaked(int i) {
57              return i;
58          }
59      }
60  
61      /**
62       * The Class ClassToBeFaked.
63       */
64      public static class ClassToBeFaked extends BaseClassToBeFaked {
65  
66          /**
67           * Instantiates a new class to be faked.
68           */
69          public ClassToBeFaked() {
70              name = "";
71          }
72  
73          /**
74           * Instantiates a new class to be faked.
75           *
76           * @param name
77           *            the name
78           */
79          public ClassToBeFaked(String name) {
80              this.name = name;
81          }
82  
83          /**
84           * Method to be faked.
85           *
86           * @return true, if successful
87           */
88          public boolean methodToBeFaked() {
89              return true;
90          }
91  
92          /**
93           * Method to be faked.
94           *
95           * @param i
96           *            the i
97           * @param args
98           *            the args
99           *
100          * @return the int
101          */
102         protected int methodToBeFaked(int i, Object... args) {
103             int result = i;
104 
105             for (Object arg : args) {
106                 if (arg != null) {
107                     result++;
108                 }
109             }
110 
111             return result;
112         }
113 
114         /**
115          * Another method to be faked.
116          *
117          * @param s
118          *            the s
119          * @param b
120          *            the b
121          * @param ints
122          *            the ints
123          *
124          * @return the string
125          */
126         public String anotherMethodToBeFaked(String s, boolean b, List<Integer> ints) {
127             return (b ? s.toUpperCase() : s.toLowerCase()) + ints;
128         }
129 
130         /**
131          * Static method to be faked.
132          *
133          * @return true, if successful
134          *
135          * @throws FileNotFoundException
136          *             the file not found exception
137          */
138         public static boolean staticMethodToBeFaked() throws FileNotFoundException {
139             throw new FileNotFoundException();
140         }
141 
142         /**
143          * Native method.
144          */
145         protected static native void nativeMethod();
146     }
147 
148     /**
149      * Proceed from fake method without parameters.
150      */
151     @Test
152     void proceedFromFakeMethodWithoutParameters() {
153         new MockUp<ClassToBeFaked>() {
154             @Mock
155             boolean methodToBeMocked(Invocation inv) {
156                 return inv.proceed();
157             }
158         };
159 
160         assertTrue(new ClassToBeFaked().methodToBeFaked());
161     }
162 
163     /**
164      * Proceed from fake method with parameters.
165      */
166     @Test
167     void proceedFromFakeMethodWithParameters() {
168         new MockUp<ClassToBeFaked>() {
169             @Mock
170             int methodToBeFaked(Invocation inv, int i) {
171                 Integer j = inv.proceed();
172                 return j + 1;
173             }
174 
175             @Mock
176             private int methodToBeFaked(Invocation inv, int i, Object... args) {
177                 args[2] = "mock";
178                 return inv.<Integer> proceed();
179             }
180         };
181 
182         ClassToBeFaked faked = new ClassToBeFaked();
183 
184         assertEquals(124, faked.methodToBeFaked(123));
185         assertEquals(-8, faked.methodToBeFaked(-9));
186         assertEquals(7, faked.methodToBeFaked(3, "Test", new Object(), null, 45));
187     }
188 
189     /**
190      * Proceed conditionally from fake method.
191      */
192     @Test
193     void proceedConditionallyFromFakeMethod() {
194         new MockUp<ClassToBeFaked>() {
195             @Mock
196             String anotherMethodToBeFaked(Invocation inv, String s, boolean b, List<Number> ints) {
197                 if (!b) {
198                     return s;
199                 }
200 
201                 ints.add(45);
202                 return inv.proceed();
203             }
204         };
205 
206         ClassToBeFaked mocked = new ClassToBeFaked();
207 
208         // Do not proceed:
209         assertNull(mocked.anotherMethodToBeFaked(null, false, null));
210 
211         // Do proceed:
212         List<Integer> values = new ArrayList<>();
213         assertEquals("TEST[45]", mocked.anotherMethodToBeFaked("test", true, values));
214 
215         // Do not proceed again:
216         assertEquals("No proceed", mocked.anotherMethodToBeFaked("No proceed", false, null));
217     }
218 
219     /**
220      * Proceed from fake method which throws checked exception.
221      *
222      * @throws Exception
223      *             the exception
224      */
225     @Test
226     void proceedFromFakeMethodWhichThrowsCheckedException() throws Exception {
227         new MockUp<ClassToBeFaked>() {
228             @Mock
229             boolean staticMethodToBeFaked(Invocation inv) throws Exception {
230                 if (inv.getInvocationIndex() == 0) {
231                     return inv.<Boolean> proceed();
232                 }
233 
234                 throw new InterruptedException("fake");
235             }
236         };
237 
238         try {
239             ClassToBeFaked.staticMethodToBeFaked();
240             fail();
241         } catch (FileNotFoundException ignored) {
242         }
243 
244         assertThrows(InterruptedException.class, () -> {
245             ClassToBeFaked.staticMethodToBeFaked();
246         });
247     }
248 
249     /**
250      * Proceed from fake method into real method with modified arguments.
251      */
252     @Test
253     void proceedFromFakeMethodIntoRealMethodWithModifiedArguments() {
254         class FakeWhichModifiesArguments extends MockUp<ClassToBeFaked> {
255             @Mock
256             final int methodToBeFaked(Invocation invocation, int i) {
257                 return invocation.<Integer> proceed(i + 2);
258             }
259         }
260 
261         new FakeWhichModifiesArguments() {
262             @Mock
263             synchronized int methodToBeFaked(Invocation inv, int i, Object... args) {
264                 Object[] newArgs = { 2, "3" };
265                 return inv.<Integer> proceed(1, newArgs);
266             }
267         };
268 
269         ClassToBeFaked faked = new ClassToBeFaked();
270         assertEquals(3, faked.methodToBeFaked(1));
271         assertEquals(3, faked.methodToBeFaked(-2, null, "Abc", true, 'a'));
272     }
273 
274     /**
275      * Cannot proceed from fake method into native method.
276      */
277     @Test
278     void cannotProceedFromFakeMethodIntoNativeMethod() {
279         new MockUp<ClassToBeFaked>() {
280             @Mock
281             void nativeMethod(Invocation inv) {
282                 inv.proceed();
283                 fail("Should not get here");
284             }
285         };
286 
287         Throwable throwable = assertThrows(UnsupportedOperationException.class, () -> {
288             ClassToBeFaked.nativeMethod();
289         });
290         assertEquals("Cannot proceed into real implementation of native method", throwable.getMessage());
291     }
292 
293     /**
294      * Proceed from fake method into constructor.
295      */
296     @Test
297     void proceedFromFakeMethodIntoConstructor() {
298         new MockUp<ClassToBeFaked>() {
299             @Mock
300             void $init(Invocation inv) {
301                 assertNotNull(inv.<ClassToBeFaked> getInvokedInstance());
302                 inv.proceed();
303             }
304         };
305 
306         ClassToBeFaked obj = new ClassToBeFaked();
307         assertEquals("", obj.name);
308     }
309 
310     /**
311      * Proceed conditionally from fake method into constructor.
312      */
313     @Test
314     void proceedConditionallyFromFakeMethodIntoConstructor() {
315         new MockUp<ClassToBeFaked>() {
316             @Mock
317             void $init(Invocation inv, String name) {
318                 assertNotNull(inv.getInvokedInstance());
319 
320                 if ("proceed".equals(name)) {
321                     inv.proceed();
322                 }
323             }
324         };
325 
326         assertEquals("proceed", new ClassToBeFaked("proceed").name);
327         assertNull(new ClassToBeFaked("do not proceed").name);
328     }
329 
330     /**
331      * Proceed conditionally from fake method into JRE constructor.
332      */
333     @Test
334     void proceedConditionallyFromFakeMethodIntoJREConstructor() {
335         new MockUp<File>() {
336             @Mock
337             void $init(Invocation inv, String name) {
338                 if ("proceed".equals(name)) {
339                     inv.proceed();
340                 }
341             }
342         };
343 
344         assertEquals("proceed", Path.of("proceed").toFile().toString());
345         assertNull(Path.of("do not proceed").toFile().toString());
346     }
347 
348     /**
349      * Proceed from fake method into method inherited from base class.
350      */
351     @Test
352     void proceedFromFakeMethodIntoMethodInheritedFromBaseClass() {
353         new MockUp<ClassToBeFaked>() {
354             @Mock
355             int baseMethod(Invocation inv, int i) {
356                 return inv.proceed(i + 1);
357             }
358         };
359 
360         assertEquals(3, new ClassToBeFaked().baseMethod(1));
361     }
362 }