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.assertTrue;
10  import static org.junit.jupiter.api.Assertions.fail;
11  
12  import java.util.Collection;
13  import java.util.Collections;
14  import java.util.List;
15  
16  import org.junit.jupiter.api.Test;
17  
18  /**
19   * The Class FakeForGenericsTest.
20   */
21  final class FakeForGenericsTest {
22  
23      /**
24       * The Class Collaborator.
25       */
26      public static final class Collaborator {
27  
28          /**
29           * Generic method.
30           *
31           * @param <N>
32           *            the number type
33           * @param n
34           *            the n
35           *
36           * @return the n
37           */
38          public <N extends Number> N genericMethod(@SuppressWarnings("UnusedParameters") N n) {
39              return null;
40          }
41      }
42  
43      /**
44       * Fake generic method.
45       */
46      @Test
47      void fakeGenericMethod() {
48          new MockUp<Collaborator>() {
49              @Mock
50              <T extends Number> T genericMethod(T t) {
51                  return t;
52              }
53  
54              // This also works (same erasure):
55              // @Mock Number genericMethod(Number t) { return t; }
56          };
57  
58          Integer n = new Collaborator().genericMethod(123);
59          assertEquals(123, n.intValue());
60  
61          Long l = new Collaborator().genericMethod(45L);
62          assertEquals(45L, l.longValue());
63  
64          Short s = new Collaborator().genericMethod((short) 6);
65          assertEquals(6, s.shortValue());
66  
67          Double d = new Collaborator().genericMethod(0.5);
68          assertEquals(0.5, d, 0);
69      }
70  
71      /**
72       * The Class GenericClass.
73       *
74       * @param <T1>
75       *            the generic type
76       * @param <T2>
77       *            the generic type
78       */
79      @SuppressWarnings("UnusedParameters")
80      public static final class GenericClass<T1, T2> {
81  
82          /**
83           * A method.
84           *
85           * @param t
86           *            the t
87           */
88          public void aMethod(T1 t) {
89              throw new RuntimeException("t=" + t);
90          }
91  
92          /**
93           * Another method.
94           *
95           * @param t
96           *            the t
97           * @param i
98           *            the i
99           * @param p
100          *            the p
101          *
102          * @return the int
103          */
104         public int anotherMethod(T1 t, int i, T2 p) {
105             return 2 * i;
106         }
107 
108         /**
109          * Another method.
110          *
111          * @param t
112          *            the t
113          * @param i
114          *            the i
115          * @param p
116          *            the p
117          *
118          * @return the int
119          */
120         public int anotherMethod(Integer t, int i, String p) {
121             return -2 * i;
122         }
123     }
124 
125     /**
126      * Fake generic class with unspecified type arguments.
127      */
128     @Test
129     void fakeGenericClassWithUnspecifiedTypeArguments() {
130         new MockUp<GenericClass<?, ?>>() {
131             @Mock
132             void aMethod(Object o) {
133                 StringBuilder s = (StringBuilder) o;
134                 s.setLength(0);
135                 s.append("fake");
136                 s.toString();
137             }
138 
139             @Mock
140             int anotherMethod(Object o, int i, Object list) {
141                 assertTrue(o instanceof StringBuilder);
142                 // noinspection unchecked
143                 assertEquals(0, ((Collection<String>) list).size());
144                 return -i;
145             }
146         };
147 
148         StringBuilder s = new StringBuilder("test");
149         GenericClass<StringBuilder, List<String>> g = new GenericClass<>();
150 
151         g.aMethod(s);
152         int r1 = g.anotherMethod(new StringBuilder("test"), 58, Collections.<String> emptyList());
153         int r2 = g.anotherMethod(123, 65, "abc");
154 
155         assertEquals("fake", s.toString());
156         assertEquals(-58, r1);
157         assertEquals(-130, r2);
158     }
159 
160     /**
161      * Fake both generic and non generic methods in generic class.
162      */
163     @Test
164     void fakeBothGenericAndNonGenericMethodsInGenericClass() {
165         new MockUp<GenericClass<String, Boolean>>() {
166             @Mock
167             int anotherMethod(Integer t, int i, String p) {
168                 return 2;
169             }
170 
171             @Mock
172             int anotherMethod(String t, int i, Boolean p) {
173                 return 1;
174             }
175         };
176 
177         GenericClass<String, Boolean> o = new GenericClass<>();
178         assertEquals(1, o.anotherMethod("generic", 1, true));
179         assertEquals(2, o.anotherMethod(123, 2, "non generic"));
180     }
181 
182     /**
183      * The Class GenericBaseClass.
184      *
185      * @param <T>
186      *            the generic type
187      * @param <U>
188      *            the generic type
189      */
190     static class GenericBaseClass<T, U> {
191         /**
192          * Find.
193          *
194          * @param id
195          *            the id
196          *
197          * @return the u
198          */
199         public U find(@SuppressWarnings("UnusedParameters") T id) {
200             return null;
201         }
202     }
203 
204     /**
205      * Fake generic method with fake method having parameter types matching type arguments.
206      */
207     @Test
208     void fakeGenericMethodWithFakeMethodHavingParameterTypesMatchingTypeArguments() {
209         new MockUp<GenericBaseClass<String, Integer>>() {
210             @Mock
211             Integer find(String id) {
212                 return id.hashCode();
213             }
214         };
215 
216         int i = new GenericBaseClass<String, Integer>().find("test");
217         assertEquals("test".hashCode(), i);
218     }
219 
220     /**
221      * Cannot call generic method when some fake method expects different types.
222      */
223     @Test
224     void cannotCallGenericMethodWhenSomeFakeMethodExpectsDifferentTypes() {
225         new MockUp<GenericBaseClass<String, Integer>>() {
226             @Mock
227             Integer find(String id) {
228                 return 1;
229             }
230         };
231 
232         try {
233             new GenericBaseClass<Integer, String>().find(1);
234             fail();
235         } catch (IllegalArgumentException e) {
236             assertTrue(e.getMessage().startsWith("Failure to invoke method: "));
237         }
238     }
239 
240     /**
241      * The Class NonGenericSuperclass.
242      */
243     static class NonGenericSuperclass extends GenericBaseClass<Integer, String> {
244     }
245 
246     /**
247      * The Class NonGenericSubclass.
248      */
249     static final class NonGenericSubclass extends NonGenericSuperclass {
250     }
251 
252     /**
253      * Fake generic method from instantiation of non generic subclass.
254      */
255     @Test
256     void fakeGenericMethodFromInstantiationOfNonGenericSubclass() {
257         new MockUp<NonGenericSubclass>() {
258             @Mock
259             String find(Integer id) {
260                 return "faked" + id;
261             }
262         };
263 
264         String s = new NonGenericSubclass().find(1);
265         assertEquals("faked1", s);
266     }
267 
268     /**
269      * The Class GenericSuperclass.
270      *
271      * @param <I>
272      *            the generic type
273      */
274     static class GenericSuperclass<I> extends GenericBaseClass<I, String> {
275     }
276 
277     /**
278      * The Class AnotherNonGenericSubclass.
279      */
280     static final class AnotherNonGenericSubclass extends GenericSuperclass<Integer> {
281     }
282 
283     /**
284      * Fake generic method from instantiation of non generic subclass which extends A generic intermediate superclass.
285      */
286     @Test
287     void fakeGenericMethodFromInstantiationOfNonGenericSubclassWhichExtendsAGenericIntermediateSuperclass() {
288         new MockUp<AnotherNonGenericSubclass>() {
289             @Mock
290             String find(Integer id) {
291                 return "faked" + id;
292             }
293         };
294 
295         String s = new AnotherNonGenericSubclass().find(1);
296         assertEquals("faked1", s);
297     }
298 
299     /**
300      * The Class NonGenericClassWithGenericMethods.
301      */
302     @SuppressWarnings("UnusedParameters")
303     public static class NonGenericClassWithGenericMethods {
304 
305         /**
306          * Static method.
307          *
308          * @param <T>
309          *            the generic type
310          * @param cls
311          *            the cls
312          * @param s
313          *            the s
314          *
315          * @return the t
316          */
317         public static <T> T staticMethod(Class<T> cls, String s) {
318             throw new RuntimeException();
319         }
320 
321         /**
322          * Instance method.
323          *
324          * @param <C>
325          *            the generic type
326          * @param cls
327          *            the cls
328          * @param s
329          *            the s
330          */
331         public <C> void instanceMethod(Class<C> cls, String s) {
332             throw new RuntimeException();
333         }
334 
335         /**
336          * Instance method.
337          *
338          * @param <N>
339          *            the number type
340          * @param cls
341          *            the cls
342          */
343         public final <N extends Number> void instanceMethod(Class<N> cls) {
344             throw new RuntimeException();
345         }
346     }
347 
348     /**
349      * Fake generic methods of non generic class.
350      */
351     @Test
352     void fakeGenericMethodsOfNonGenericClass() {
353         new MockUp<NonGenericClassWithGenericMethods>() {
354             @Mock
355             <T> T staticMethod(Class<T> cls, String s) {
356                 return null;
357             }
358 
359             @Mock
360             <C> void instanceMethod(Class<C> cls, String s) {
361             }
362 
363             @Mock
364             void instanceMethod(Class<?> cls) {
365             }
366         };
367 
368         new NonGenericClassWithGenericMethods().instanceMethod(Integer.class);
369         NonGenericClassWithGenericMethods.staticMethod(Collaborator.class, "test1");
370         new NonGenericClassWithGenericMethods().instanceMethod(Byte.class, "test2");
371     }
372 }