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 mockit.integration.junit5.JMockitExtension;
17  
18  import org.junit.jupiter.api.Test;
19  import org.junit.jupiter.api.extension.ExtendWith;
20  
21  @ExtendWith(JMockitExtension.class)
22  class MockUpForGenericsTest {
23      // Mock-ups for generic classes/methods ////////////////////////////////////////////////////////////////////////////
24  
25      public static final class Collaborator {
26          public <N extends Number> N genericMethod(@SuppressWarnings("UnusedParameters") N n) {
27              return null;
28          }
29      }
30  
31      @Test
32      void mockGenericMethod() {
33          new MockUp<Collaborator>() {
34              @Mock
35              <T extends Number> T genericMethod(T t) {
36                  return t;
37              }
38  
39              // This also works (same erasure):
40              // @Mock Number genericMethod(Number t) { return t; }
41          };
42  
43          Integer n = new Collaborator().genericMethod(123);
44          assertEquals(123, n.intValue());
45  
46          Long l = new Collaborator().genericMethod(45L);
47          assertEquals(45L, l.longValue());
48  
49          Short s = new Collaborator().genericMethod((short) 6);
50          assertEquals(6, s.shortValue());
51  
52          Double d = new Collaborator().genericMethod(0.5);
53          assertEquals(0.5, d, 0);
54      }
55  
56      @SuppressWarnings("UnusedParameters")
57      public static final class GenericClass<T1, T2> {
58          public void aMethod(T1 t) {
59              throw new RuntimeException("t=" + t);
60          }
61  
62          public int anotherMethod(T1 t, int i, T2 p) {
63              return 2 * i;
64          }
65  
66          public int anotherMethod(Integer t, int i, String p) {
67              return -2 * i;
68          }
69      }
70  
71      @Test
72      void mockGenericClassWithUnspecifiedTypeArguments() {
73          new MockUp<GenericClass<?, ?>>() {
74              @Mock
75              void aMethod(Object o) {
76                  StringBuilder s = (StringBuilder) o;
77                  s.setLength(0);
78                  s.append("mock");
79                  s.toString();
80              }
81  
82              @Mock
83              int anotherMethod(Object o, int i, Object list) {
84                  assertTrue(o instanceof StringBuilder);
85                  // noinspection unchecked
86                  assertEquals(0, ((Collection<String>) list).size());
87                  return -i;
88              }
89          };
90  
91          StringBuilder s = new StringBuilder("test");
92          GenericClass<StringBuilder, List<String>> g = new GenericClass<>();
93  
94          g.aMethod(s);
95          int r1 = g.anotherMethod(new StringBuilder("test"), 58, Collections.<String> emptyList());
96          int r2 = g.anotherMethod(123, 65, "abc");
97  
98          assertEquals("mock", s.toString());
99          assertEquals(-58, r1);
100         assertEquals(-130, r2);
101     }
102 
103     @Test
104     void mockBothGenericAndNonGenericMethodsInGenericClass() {
105         new MockUp<GenericClass<String, Boolean>>() {
106             @Mock
107             int anotherMethod(Integer t, int i, String p) {
108                 return 2;
109             }
110 
111             @Mock
112             int anotherMethod(String t, int i, Boolean p) {
113                 return 1;
114             }
115         };
116 
117         GenericClass<String, Boolean> o = new GenericClass<>();
118         assertEquals(1, o.anotherMethod("generic", 1, true));
119         assertEquals(2, o.anotherMethod(123, 2, "non generic"));
120     }
121 
122     static class GenericBaseClass<T, U> {
123         public U find(@SuppressWarnings("UnusedParameters") T id) {
124             return null;
125         }
126     }
127 
128     @Test
129     void mockGenericMethodWithMockMethodHavingParameterTypesMatchingTypeArguments() {
130         new MockUp<GenericBaseClass<String, Integer>>() {
131             @Mock
132             Integer find(String id) {
133                 return id.hashCode();
134             }
135         };
136 
137         int i = new GenericBaseClass<String, Integer>().find("test");
138         assertEquals("test".hashCode(), i);
139     }
140 
141     @Test
142     void cannotCallGenericMethodWhenSomeMockMethodExpectsDifferentTypes() {
143         new MockUp<GenericBaseClass<String, Integer>>() {
144             @Mock
145             Integer find(String id) {
146                 return 1;
147             }
148         };
149 
150         try {
151             new GenericBaseClass<Integer, String>().find(1);
152             fail();
153         } catch (IllegalArgumentException e) {
154             assertTrue(e.getMessage().startsWith("Failure to invoke method: "));
155         }
156     }
157 
158     static class NonGenericSuperclass extends GenericBaseClass<Integer, String> {
159     }
160 
161     final class NonGenericSubclass extends NonGenericSuperclass {
162     }
163 
164     @Test
165     void mockGenericMethodFromInstantiationOfNonGenericSubclass() {
166         new MockUp<NonGenericSubclass>() {
167             @Mock
168             String find(Integer id) {
169                 return "mocked" + id;
170             }
171         };
172 
173         String s = new NonGenericSubclass().find(1);
174         assertEquals("mocked1", s);
175     }
176 
177     static class GenericSuperclass<I> extends GenericBaseClass<I, String> {
178     }
179 
180     final class AnotherNonGenericSubclass extends GenericSuperclass<Integer> {
181     }
182 
183     @Test
184     void mockGenericMethodFromInstantiationOfNonGenericSubclassWhichExtendsAGenericIntermediateSuperclass() {
185         new MockUp<AnotherNonGenericSubclass>() {
186             @Mock
187             String find(Integer id) {
188                 return "mocked" + id;
189             }
190         };
191 
192         String s = new AnotherNonGenericSubclass().find(1);
193         assertEquals("mocked1", s);
194     }
195 
196     @SuppressWarnings("UnusedParameters")
197     public static class NonGenericClassWithGenericMethods {
198         public static <T> T staticMethod(Class<T> cls, String s) {
199             throw new RuntimeException();
200         }
201 
202         public <C> void instanceMethod(Class<C> cls, String s) {
203             throw new RuntimeException();
204         }
205 
206         public final <N extends Number> void instanceMethod(Class<N> cls) {
207             throw new RuntimeException();
208         }
209     }
210 
211     @Test
212     void mockGenericMethodsOfNonGenericClass() {
213         new MockUp<NonGenericClassWithGenericMethods>() {
214             @Mock
215             <T> T staticMethod(Class<T> cls, String s) {
216                 return null;
217             }
218 
219             @Mock
220             <C> void instanceMethod(Class<C> cls, String s) {
221             }
222 
223             @Mock
224             void instanceMethod(Class<?> cls) {
225             }
226         };
227 
228         new NonGenericClassWithGenericMethods().instanceMethod(Integer.class);
229         NonGenericClassWithGenericMethods.staticMethod(Collaborator.class, "test1");
230         new NonGenericClassWithGenericMethods().instanceMethod(Byte.class, "test2");
231     }
232 
233     // Mock-ups for generic interfaces /////////////////////////////////////////////////////////////////////////////////
234 
235     public interface GenericInterface<T> {
236         void method(T t);
237     }
238 
239     @Test
240     void mockGenericInterfaceMethodWithMockMethodHavingParameterOfTypeObject() {
241         GenericInterface<Boolean> mock = new MockUp<GenericInterface<Boolean>>() {
242             @Mock
243             void method(Object b) {
244                 assertTrue((Boolean) b);
245             }
246         }.getMockInstance();
247 
248         mock.method(true);
249     }
250 
251     public interface NonGenericSubInterface extends GenericInterface<Long> {
252     }
253 
254     @Test
255     void mockMethodOfSubInterfaceWithGenericTypeArgument() {
256         NonGenericSubInterface mock = new MockUp<NonGenericSubInterface>() {
257             @Mock
258             void method(Long l) {
259                 assertTrue(l > 0);
260             }
261         }.getMockInstance();
262 
263         mock.method(123L);
264     }
265 
266     @Test
267     void mockGenericInterfaceMethod() {
268         Comparable<Integer> cmp = new MockUp<Comparable<Integer>>() {
269             @Mock
270             int compareTo(Integer i) {
271                 assertEquals(123, i.intValue());
272                 return 2;
273             }
274         }.getMockInstance();
275 
276         assertEquals(2, cmp.compareTo(123));
277     }
278 }