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 java.util.Arrays.asList;
9   
10  import static org.junit.jupiter.api.Assertions.assertEquals;
11  import static org.junit.jupiter.api.Assertions.assertSame;
12  import static org.junit.jupiter.api.Assertions.fail;
13  
14  import java.lang.reflect.Member;
15  import java.lang.reflect.Method;
16  import java.util.ArrayList;
17  import java.util.Arrays;
18  import java.util.List;
19  
20  import org.junit.jupiter.api.Test;
21  
22  /**
23   * The Class FakingEverythingTest.
24   */
25  final class FakingEverythingTest {
26  
27      /** The traces. */
28      final List<String> traces = new ArrayList<>();
29  
30      /**
31       * Trace entry.
32       *
33       * @param inv
34       *            the inv
35       */
36      void traceEntry(Invocation inv) {
37          traces.add("Entered " + getDescription(inv));
38      }
39  
40      /**
41       * Trace exit.
42       *
43       * @param inv
44       *            the inv
45       */
46      void traceExit(Invocation inv) {
47          traces.add("Exited " + getDescription(inv));
48      }
49  
50      /**
51       * Gets the description.
52       *
53       * @param inv
54       *            the inv
55       *
56       * @return the description
57       */
58      String getDescription(Invocation inv) {
59          Member member = inv.getInvokedMember();
60          String args = Arrays.toString(inv.getInvokedArguments());
61          Object instance = inv.getInvokedInstance();
62          return member.getDeclaringClass().getSimpleName() + '#' + member.getName() + " with " + args + " on "
63                  + instance;
64      }
65  
66      /**
67       * Fake every method in single class.
68       */
69      @Test
70      void fakeEveryMethodInSingleClass() {
71          new MockUp<TargetClass>() {
72              @Mock
73              Object $advice(Invocation inv) {
74                  traceEntry(inv);
75  
76                  try {
77                      return inv.proceed();
78                  } finally {
79                      traceExit(inv);
80                  }
81              }
82  
83              @Mock
84              void validateSomething(Invocation inv) {
85                  Method m = inv.getInvokedMember();
86                  assertEquals("validateSomething", m.getName());
87              }
88          };
89  
90          TargetClass.staticMethod(123);
91          final TargetClass tc0 = new TargetClass();
92          assertEquals(4, tc0.doSomething("test", true));
93          tc0.performAction(new Runnable() {
94              @Override
95              public void run() {
96                  tc0.doSomething("internal", false);
97              }
98  
99              @Override
100             public String toString() {
101                 return "action";
102             }
103         });
104         TargetClass tc1 = new TargetClass(1);
105         tc1.performAction(null);
106         tc1.validateSomething();
107 
108         List<String> expectedTraces = asList("Entered TargetClass#staticMethod with [123] on null",
109                 "Exited TargetClass#staticMethod with [123] on null",
110                 "Entered TargetClass#doSomething with [test, true] on TargetClass0",
111                 "Exited TargetClass#doSomething with [test, true] on TargetClass0",
112                 "Entered TargetClass#performAction with [action] on TargetClass0",
113                 "Entered TargetClass#doSomething with [internal, false] on TargetClass0",
114                 "Exited TargetClass#doSomething with [internal, false] on TargetClass0",
115                 "Exited TargetClass#performAction with [action] on TargetClass0",
116                 "Entered TargetClass#performAction with [null] on TargetClass1",
117                 "Exited TargetClass#performAction with [null] on TargetClass1");
118         assertEquals(expectedTraces, traces);
119     }
120 
121     /**
122      * Fake every method in single class with advice only.
123      */
124     @Test
125     void fakeEveryMethodInSingleClassWithAdviceOnly() {
126         new MockUp<TargetClass>() {
127             @Mock
128             Object $advice(Invocation inv) {
129                 Integer i = inv.proceed();
130                 return i + 2;
131             }
132         };
133 
134         assertEquals(1, new TargetClass().doSomething("", false));
135     }
136 
137     /**
138      * Fake every method in class hierarchy.
139      *
140      * @param <B>
141      *            the generic type
142      */
143     @Test
144     <B extends TargetClass> void fakeEveryMethodInClassHierarchy() {
145         new MockUp<B>() {
146             @Mock
147             Object $advice(Invocation inv) {
148                 traceEntry(inv);
149 
150                 try {
151                     return inv.proceed();
152                 } finally {
153                     traceExit(inv);
154                 }
155             }
156         };
157 
158         final TargetSubclass s1 = new TargetSubclass(1);
159         assertEquals(4, s1.doSomething("test", true));
160         assertEquals("123", s1.additionalMethod(123));
161         s1.performAction(new Runnable() {
162             @Override
163             public void run() {
164                 assertSame(s1, this);
165             }
166 
167             @Override
168             public String toString() {
169                 return "sub-action";
170             }
171         });
172 
173         TargetClass s2 = new TargetClass(2);
174         s2.performAction(null);
175 
176         try {
177             s2.validateSomething();
178             fail();
179         } catch (IllegalArgumentException e) {
180             assertEquals("Invalid something", e.getMessage());
181         }
182 
183         List<String> expectedTraces = asList("Entered TargetClass#doSomething with [test, true] on TargetSubclass1",
184                 "Exited TargetClass#doSomething with [test, true] on TargetSubclass1",
185                 "Entered TargetSubclass#additionalMethod with [123] on TargetSubclass1",
186                 "Exited TargetSubclass#additionalMethod with [123] on TargetSubclass1",
187                 "Entered TargetSubclass#performAction with [sub-action] on TargetSubclass1",
188                 "Entered TargetSubclass#additionalMethod with [45] on TargetSubclass1",
189                 "Exited TargetSubclass#additionalMethod with [45] on TargetSubclass1",
190                 "Exited TargetSubclass#performAction with [sub-action] on TargetSubclass1",
191                 "Entered TargetClass#performAction with [null] on TargetClass2",
192                 "Exited TargetClass#performAction with [null] on TargetClass2",
193                 "Entered TargetClass#validateSomething with [] on TargetClass2",
194                 "Exited TargetClass#validateSomething with [] on TargetClass2");
195         assertEquals(expectedTraces, traces);
196     }
197 
198     /**
199      * The Class PublicFake.
200      */
201     public static final class PublicFake extends MockUp<TargetClass> {
202 
203         /**
204          * $advice.
205          *
206          * @param inv
207          *            the inv
208          *
209          * @return the object
210          */
211         @Mock
212         public static Object $advice(Invocation inv) {
213             Object[] args = inv.getInvokedArguments();
214 
215             if (args.length > 0) {
216                 Integer i = (Integer) args[0];
217                 return -i;
218             }
219 
220             return null;
221         }
222     }
223 
224     /**
225      * Public advice method in public fake class.
226      */
227     @Test
228     void publicAdviceMethodInPublicFakeClass() {
229         new PublicFake();
230 
231         new TargetClass().validateSomething();
232         int i = TargetClass.staticMethod(123);
233 
234         assertEquals(-123, i);
235     }
236 }
237 
238 class TargetClass {
239     final int value;
240 
241     TargetClass() {
242         value = 0;
243     }
244 
245     TargetClass(int value) {
246         this.value = value;
247     }
248 
249     public static int staticMethod(int i) {
250         return i;
251     }
252 
253     int doSomething(String s, boolean b) {
254         return b ? s.length() : -1;
255     }
256 
257     protected void performAction(Runnable action) {
258         if (action != null) {
259             action.run();
260         }
261     }
262 
263     protected void validateSomething() {
264         throw new IllegalArgumentException("Invalid something");
265     }
266 
267     @Override
268     public String toString() {
269         return getClass().getSimpleName() + value;
270     }
271 }
272 
273 final class TargetSubclass extends TargetClass {
274     TargetSubclass(int value) {
275         super(value);
276     }
277 
278     String additionalMethod(int i) {
279         return String.valueOf(i);
280     }
281 
282     @Override
283     protected void performAction(Runnable action) {
284         additionalMethod(45);
285         super.performAction(action);
286     }
287 }