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.assertFalse;
10  import static org.junit.jupiter.api.Assertions.assertNotNull;
11  import static org.junit.jupiter.api.Assertions.assertNull;
12  import static org.junit.jupiter.api.Assertions.assertSame;
13  import static org.junit.jupiter.api.Assertions.assertThrows;
14  import static org.junit.jupiter.api.Assertions.assertTrue;
15  
16  import jakarta.faces.application.FacesMessage;
17  
18  import java.lang.reflect.Method;
19  
20  import javax.accessibility.AccessibleState;
21  
22  import mockit.MoreFakingTest.ClassWithNested.Nested;
23  
24  import org.junit.jupiter.api.Test;
25  
26  /**
27   * The Class MoreFakingTest.
28   */
29  @SuppressWarnings("JUnitTestMethodWithNoAssertions")
30  final class MoreFakingTest {
31  
32      /** The code under test. */
33      final CodeUnderTest codeUnderTest = new CodeUnderTest();
34  
35      /** The fake executed. */
36      boolean fakeExecuted;
37  
38      /**
39       * The Class CodeUnderTest.
40       */
41      static class CodeUnderTest {
42  
43          /** The dependency. */
44          private final Collaborator dependency = new Collaborator();
45  
46          /**
47           * Do something.
48           */
49          void doSomething() {
50              dependency.provideSomeService();
51          }
52  
53          /**
54           * Do something else.
55           *
56           * @param i
57           *            the i
58           *
59           * @return the long
60           */
61          long doSomethingElse(int i) {
62              return dependency.getThreadSpecificValue(i);
63          }
64      }
65  
66      /**
67       * The Class Collaborator.
68       */
69      public static class Collaborator {
70  
71          /** The xyz. */
72          static Object xyz;
73  
74          /** The value. */
75          protected int value;
76  
77          /**
78           * Instantiates a new collaborator.
79           */
80          public Collaborator() {
81          }
82  
83          /**
84           * Instantiates a new collaborator.
85           *
86           * @param value
87           *            the value
88           */
89          Collaborator(int value) {
90              this.value = value;
91          }
92  
93          /**
94           * Do internal.
95           *
96           * @return the string
97           */
98          @SuppressWarnings("DeprecatedIsStillUsed")
99          @Deprecated
100         protected static String doInternal() {
101             return "123";
102         }
103 
104         /**
105          * Provide some service.
106          */
107         public void provideSomeService() {
108             throw new RuntimeException("Real provideSomeService() called");
109         }
110 
111         /**
112          * Gets the value.
113          *
114          * @return the value
115          */
116         public int getValue() {
117             return value;
118         }
119 
120         /**
121          * Sets the value.
122          *
123          * @param value
124          *            the new value
125          */
126         void setValue(int value) {
127             this.value = value;
128         }
129 
130         /**
131          * Gets the thread specific value.
132          *
133          * @param i
134          *            the i
135          *
136          * @return the thread specific value
137          */
138         protected long getThreadSpecificValue(int i) {
139             return Thread.currentThread().getId() + i;
140         }
141     }
142 
143     /**
144      * The Class FakeCollaborator1.
145      */
146     static class FakeCollaborator1 extends MockUp<Collaborator> {
147 
148         /**
149          * Provide some service.
150          */
151         @Mock
152         void provideSomeService() {
153         }
154     }
155 
156     /**
157      * Fake doing nothing.
158      */
159     @Test
160     void fakeDoingNothing() {
161         new FakeCollaborator1();
162 
163         codeUnderTest.doSomething();
164     }
165 
166     /**
167      * Apply fakes from inner fake class with fake constructor.
168      */
169     @Test
170     void applyFakesFromInnerFakeClassWithFakeConstructor() {
171         new FakeCollaborator4();
172         assertFalse(fakeExecuted);
173 
174         new CodeUnderTest().doSomething();
175 
176         assertTrue(fakeExecuted);
177     }
178 
179     /**
180      * The Class FakeCollaborator4.
181      */
182     class FakeCollaborator4 extends MockUp<Collaborator> {
183 
184         /**
185          * $init.
186          */
187         @Mock
188         void $init() {
189             fakeExecuted = true;
190         }
191 
192         /**
193          * Provide some service.
194          */
195         @Mock
196         void provideSomeService() {
197         }
198     }
199 
200     /**
201      * Apply reentrant fake.
202      */
203     @Test
204     public void applyReentrantFake() {
205         new FakeCollaboratorWithReentrantFakeMethod();
206 
207         assertThrows(RuntimeException.class, () -> {
208             codeUnderTest.doSomething();
209         });
210     }
211 
212     /**
213      * The Class FakeCollaboratorWithReentrantFakeMethod.
214      */
215     static class FakeCollaboratorWithReentrantFakeMethod extends MockUp<Collaborator> {
216 
217         /**
218          * Gets the value.
219          *
220          * @return the value
221          */
222         @Mock
223         int getValue() {
224             return 123;
225         }
226 
227         /**
228          * Provide some service.
229          *
230          * @param inv
231          *            the inv
232          */
233         @Mock
234         void provideSomeService(Invocation inv) {
235             inv.proceed();
236         }
237     }
238 
239     /**
240      * Apply fake for constructor.
241      */
242     @Test
243     void applyFakeForConstructor() {
244         new FakeCollaboratorWithConstructorFake();
245 
246         new FacesMessage("test");
247     }
248 
249     /**
250      * The Class FakeCollaboratorWithConstructorFake.
251      */
252     static class FakeCollaboratorWithConstructorFake extends MockUp<FacesMessage> {
253 
254         /**
255          * $init.
256          *
257          * @param value
258          *            the value
259          */
260         @Mock
261         void $init(String value) {
262             assertEquals("test", value);
263         }
264     }
265 
266     /**
267      * The Class SubCollaborator.
268      */
269     public static class SubCollaborator extends Collaborator {
270 
271         /**
272          * Instantiates a new sub collaborator.
273          *
274          * @param i
275          *            the i
276          */
277         public SubCollaborator(int i) {
278             throw new RuntimeException(String.valueOf(i));
279         }
280 
281         @Override
282         public void provideSomeService() {
283             value = 123;
284         }
285     }
286 
287     /**
288      * Apply fake for class hierarchy.
289      */
290     @Test
291     void applyFakeForClassHierarchy() {
292         new MockUp<SubCollaborator>() {
293             @Mock
294             void $init(Invocation inv, int i) {
295                 assertNotNull(inv.getInvokedInstance());
296                 assertTrue(i > 0);
297             }
298 
299             @Mock
300             void provideSomeService(Invocation inv) {
301                 SubCollaborator it = inv.getInvokedInstance();
302                 it.value = 45;
303             }
304 
305             @Mock
306             int getValue(Invocation inv) {
307                 SubCollaborator it = inv.getInvokedInstance();
308                 assertNotNull(it);
309                 return 123;
310             }
311         };
312 
313         SubCollaborator collaborator = new SubCollaborator(123);
314         collaborator.provideSomeService();
315         assertEquals(45, collaborator.value);
316         assertEquals(123, collaborator.getValue());
317     }
318 
319     /**
320      * Apply fake for JRE class.
321      */
322     @Test
323     void applyFakeForJREClass() {
324         FakeThread fakeThread = new FakeThread();
325 
326         Thread.currentThread().interrupt();
327 
328         assertTrue(fakeThread.interrupted);
329     }
330 
331     /**
332      * The Class FakeThread.
333      */
334     public static class FakeThread extends MockUp<Thread> {
335 
336         /** The interrupted. */
337         boolean interrupted;
338 
339         /**
340          * Interrupt.
341          */
342         @Mock
343         public void interrupt() {
344             interrupted = true;
345         }
346     }
347 
348     /**
349      * Fake static initializer.
350      */
351     @Test
352     void fakeStaticInitializer() {
353         new MockUp<AccessibleState>() {
354             @Mock
355             void $clinit() {
356             }
357         };
358 
359         assertNull(AccessibleState.ACTIVE);
360     }
361 
362     /**
363      * The Class AnAbstractClass.
364      */
365     abstract static class AnAbstractClass {
366         /**
367          * Do something.
368          *
369          * @return the int
370          */
371         protected abstract int doSomething();
372     }
373 
374     /**
375      * Fake abstract class with fake for abstract method having invocation parameter.
376      *
377      * @param <A>
378      *            the generic type
379      */
380     @Test
381     <A extends AnAbstractClass> void fakeAbstractClassWithFakeForAbstractMethodHavingInvocationParameter() {
382         final AnAbstractClass obj = new AnAbstractClass() {
383             @Override
384             protected int doSomething() {
385                 return 0;
386             }
387         };
388 
389         new MockUp<A>() {
390             @Mock
391             int doSomething(Invocation inv) {
392                 assertSame(obj, inv.getInvokedInstance());
393                 Method invokedMethod = inv.getInvokedMember();
394                 assertTrue(AnAbstractClass.class.isAssignableFrom(invokedMethod.getDeclaringClass()));
395                 return 123;
396             }
397         };
398 
399         assertEquals(123, obj.doSomething());
400     }
401 
402     /**
403      * The Class GenericClass.
404      *
405      * @param <T>
406      *            the generic type
407      */
408     static class GenericClass<T> {
409         /**
410          * Do something.
411          *
412          * @return the t
413          */
414         protected T doSomething() {
415             return null;
416         }
417     }
418 
419     /**
420      * Fake generic class with fake having invocation parameter.
421      */
422     @Test
423     void fakeGenericClassWithFakeHavingInvocationParameter() {
424         new MockUp<GenericClass<String>>() {
425             @Mock
426             String doSomething(Invocation inv) {
427                 return "faked";
428             }
429         };
430 
431         GenericClass<String> faked = new GenericClass<>();
432         assertEquals("faked", faked.doSomething());
433     }
434 
435     /**
436      * Concurrent fake.
437      *
438      * @throws Exception
439      *             the exception
440      */
441     @Test
442     @SuppressWarnings("MethodWithMultipleLoops")
443     void concurrentFake() throws Exception {
444         new MockUp<Collaborator>() {
445             @Mock
446             long getThreadSpecificValue(int i) {
447                 return Thread.currentThread().getId() + 123;
448             }
449         };
450 
451         Thread[] threads = new Thread[5];
452 
453         for (int i = 0; i < threads.length; i++) {
454             threads[i] = new Thread() {
455                 @Override
456                 public void run() {
457                     long threadSpecificValue = Thread.currentThread().getId() + 123;
458                     long actualValue = new CodeUnderTest().doSomethingElse(0);
459                     assertEquals(threadSpecificValue, actualValue);
460                 }
461             };
462         }
463 
464         for (Thread thread : threads) {
465             thread.start();
466         }
467         for (Thread thread : threads) {
468             thread.join();
469         }
470     }
471 
472     /**
473      * Fake affects instances of specified subclass and not of base class.
474      */
475     @Test
476     void fakeAffectsInstancesOfSpecifiedSubclassAndNotOfBaseClass() {
477         new FakeForSubclass();
478 
479         // Faking applies to instance methods executed on instances of the subclass:
480         assertEquals(123, new SubCollaborator(5).getValue());
481 
482         // And to static methods from any class in the hierarchy:
483         // noinspection deprecation
484         assertEquals("faked", Collaborator.doInternal());
485 
486         // But not to instance methods executed on instances of the base class:
487         assertEquals(62, new Collaborator(62).getValue());
488     }
489 
490     /**
491      * The Class FakeForSubclass.
492      */
493     static class FakeForSubclass extends MockUp<SubCollaborator> {
494 
495         /**
496          * $init.
497          *
498          * @param i
499          *            the i
500          */
501         @Mock
502         void $init(int i) {
503         }
504 
505         /**
506          * Do internal.
507          *
508          * @return the string
509          */
510         @Mock
511         String doInternal() {
512             return "faked";
513         }
514 
515         /**
516          * Gets the value.
517          *
518          * @return the value
519          */
520         @Mock
521         int getValue() {
522             return 123;
523         }
524     }
525 
526     /**
527      * The Class ClassWithNested.
528      */
529     public static final class ClassWithNested {
530 
531         /**
532          * Do something.
533          */
534         public static void doSomething() {
535         }
536 
537         /**
538          * The Class Nested.
539          */
540         public static final class Nested {
541         }
542     }
543 
544     /**
545      * Fake A class having A nested class.
546      */
547     @Test
548     void fakeAClassHavingANestedClass() {
549         new MockUp<ClassWithNested>() {
550             @Mock
551             void doSomething() {
552             }
553         };
554 
555         Class<?> outerClass = Nested.class.getDeclaringClass();
556 
557         assertSame(ClassWithNested.class, outerClass);
558     }
559 }