View Javadoc
1   /*
2    * MIT License
3    * Copyright (c) 2006-2025 JMockit developers
4    * See LICENSE file for full license text.
5    */
6   package otherTests;
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.assertSame;
11  import static org.junit.jupiter.api.Assertions.assertThrows;
12  
13  import java.security.SecureRandom;
14  import java.util.ArrayList;
15  import java.util.Collections;
16  import java.util.HashSet;
17  import java.util.List;
18  import java.util.Set;
19  
20  import mockit.Deencapsulation;
21  
22  import org.junit.jupiter.api.Test;
23  
24  final class DeencapsulationTest {
25  
26      static final class Subclass extends BaseClass {
27          private static final SecureRandom SECURE_RANDOM = new SecureRandom();
28          final int INITIAL_VALUE = SECURE_RANDOM.nextInt();
29          final int initialValue = -1;
30  
31          @SuppressWarnings("unused")
32          private static final Integer constantField = 123;
33  
34          private static StringBuilder buffer;
35          @SuppressWarnings("unused")
36          private static char static1;
37          @SuppressWarnings("unused")
38          private static char static2;
39  
40          static StringBuilder getBuffer() {
41              return buffer;
42          }
43  
44          static void setBuffer(StringBuilder buffer) {
45              Subclass.buffer = buffer;
46          }
47  
48          private String stringField;
49          private int intField;
50          private int intField2;
51          private List<String> listField;
52  
53          @SuppressWarnings("unused")
54          private String privateMethod(int value) {
55              return "result-" + value;
56          }
57  
58          @SuppressWarnings("unused")
59          private static String privateStaticMethod(String arg) {
60              return "static-" + arg;
61          }
62  
63          int getIntField() {
64              return intField;
65          }
66  
67          void setIntField(int intField) {
68              this.intField = intField;
69          }
70  
71          int getIntField2() {
72              return intField2;
73          }
74  
75          void setIntField2(int intField2) {
76              this.intField2 = intField2;
77          }
78  
79          String getStringField() {
80              return stringField;
81          }
82  
83          void setStringField(String stringField) {
84              this.stringField = stringField;
85          }
86  
87          List<String> getListField() {
88              return listField;
89          }
90  
91          void setListField(List<String> listField) {
92              this.listField = listField;
93          }
94      }
95  
96      final Subclass anInstance = new Subclass();
97  
98      @Test
99      void getInstanceFieldByName() {
100         anInstance.setIntField(3);
101         anInstance.setStringField("test");
102         anInstance.setListField(Collections.<String> emptyList());
103 
104         Integer intValue = Deencapsulation.getField(anInstance, "intField");
105         String stringValue = Deencapsulation.getField(anInstance, "stringField");
106         List<String> listValue = Deencapsulation.getField(anInstance, "listField");
107 
108         assertEquals(anInstance.getIntField(), intValue.intValue());
109         assertEquals(anInstance.getStringField(), stringValue);
110         assertSame(anInstance.getListField(), listValue);
111     }
112 
113     @Test
114     public void attemptToGetInstanceFieldByNameWithWrongName() {
115         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
116             Deencapsulation.getField(anInstance, "noField");
117         });
118         assertEquals("No instance field of name \"noField\" found in class otherTests.BaseClass",
119                 throwable.getMessage());
120     }
121 
122     @Test
123     void getInheritedInstanceFieldByName() {
124         anInstance.baseInt = 3;
125         anInstance.baseString = "test";
126         anInstance.baseSet = Collections.emptySet();
127 
128         Integer intValue = Deencapsulation.getField(anInstance, "baseInt");
129         String stringValue = Deencapsulation.getField(anInstance, "baseString");
130         Set<Boolean> listValue = Deencapsulation.getField(anInstance, "baseSet");
131 
132         assertEquals(anInstance.baseInt, intValue.intValue());
133         assertEquals(anInstance.baseString, stringValue);
134         assertSame(anInstance.baseSet, listValue);
135     }
136 
137     @Test
138     @SuppressWarnings("unchecked")
139     void getInstanceFieldByType() {
140         anInstance.setStringField("by type");
141         anInstance.setListField(new ArrayList<String>());
142 
143         String stringValue = Deencapsulation.getField(anInstance, String.class);
144         List<String> listValue = Deencapsulation.getField(anInstance, List.class);
145         List<String> listValue2 = Deencapsulation.getField(anInstance, ArrayList.class);
146 
147         assertEquals(anInstance.getStringField(), stringValue);
148         assertSame(anInstance.getListField(), listValue);
149         assertSame(listValue, listValue2);
150     }
151 
152     @Test
153     public void attemptToGetInstanceFieldByTypeWithWrongType() {
154         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
155             Deencapsulation.getField(anInstance, Byte.class);
156         });
157         assertEquals("Instance field of type byte or Byte not found in class otherTests.BaseClass",
158                 throwable.getMessage());
159     }
160 
161     @Test
162     public void attemptToGetInstanceFieldByTypeForClassWithMultipleFieldsOfThatType() {
163         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
164             Deencapsulation.getField(anInstance, int.class);
165         });
166         assertEquals("More than one instance field from which a value of type int "
167                 + "can be read exists in class otherTests.DeencapsulationTest$Subclass: "
168                 + "INITIAL_VALUE, initialValue", throwable.getMessage());
169     }
170 
171     @Test
172     @SuppressWarnings("unchecked")
173     void getInheritedInstanceFieldByType() {
174         Set<Boolean> fieldValueOnInstance = new HashSet<>();
175         anInstance.baseSet = fieldValueOnInstance;
176 
177         Set<Boolean> setValue = Deencapsulation.getField(anInstance, fieldValueOnInstance.getClass());
178         Set<Boolean> setValue2 = Deencapsulation.getField(anInstance, HashSet.class);
179 
180         assertSame(fieldValueOnInstance, setValue);
181         assertSame(setValue, setValue2);
182     }
183 
184     @Test
185     void getInstanceFieldOnBaseClassByType() {
186         anInstance.setLongField(15);
187 
188         long longValue = Deencapsulation.getField(anInstance, long.class);
189 
190         assertEquals(15, longValue);
191     }
192 
193     @Test
194     void getStaticFieldByName() {
195         Subclass.setBuffer(new StringBuilder());
196 
197         StringBuilder b = Deencapsulation.getField(Subclass.class, "buffer");
198 
199         assertSame(Subclass.getBuffer(), b);
200     }
201 
202     @Test
203     public void attemptToGetStaticFieldByNameFromWrongClass() {
204         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
205             Deencapsulation.getField(BaseClass.class, "buffer");
206         });
207         assertEquals("No static field of name \"buffer\" found in class otherTests.BaseClass", throwable.getMessage());
208     }
209 
210     @Test
211     void getStaticFieldByType() {
212         Subclass.setBuffer(new StringBuilder());
213 
214         StringBuilder b = Deencapsulation.getField(Subclass.class, StringBuilder.class);
215 
216         assertSame(Subclass.getBuffer(), b);
217     }
218 
219     @Test
220     void setInstanceFieldByName() {
221         anInstance.setIntField2(1);
222 
223         Deencapsulation.setField(anInstance, "intField2", 901);
224 
225         assertEquals(901, anInstance.getIntField2());
226     }
227 
228     @Test
229     public void attemptToSetInstanceFieldByNameWithWrongName() {
230         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
231             Deencapsulation.setField(anInstance, "noField", 901);
232         });
233         assertEquals("No instance field of name \"noField\" found in class otherTests.BaseClass",
234                 throwable.getMessage());
235     }
236 
237     @Test
238     void setInstanceFieldByType() {
239         anInstance.setStringField("");
240 
241         Deencapsulation.setField(anInstance, "Test");
242 
243         assertEquals("Test", anInstance.getStringField());
244     }
245 
246     @Test
247     public void attemptToSetInstanceFieldByTypeWithWrongType() {
248         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
249             Deencapsulation.setField(anInstance, (byte) 123);
250         });
251         assertEquals("Instance field of type byte or Byte not found in class otherTests.BaseClass",
252                 throwable.getMessage());
253     }
254 
255     @Test
256     public void attemptToSetInstanceFieldByTypeForClassWithMultipleFieldsOfThatType() {
257         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
258             Deencapsulation.setField(anInstance, 901);
259         });
260         assertEquals(
261                 "More than one instance field to which a value of type int "
262                         + "or Integer can be assigned exists in class "
263                         + "otherTests.DeencapsulationTest$Subclass: INITIAL_VALUE, initialValue",
264                 throwable.getMessage());
265     }
266 
267     @Test
268     void setStaticFieldByName() {
269         Subclass.setBuffer(null);
270 
271         Deencapsulation.setField(Subclass.class, "buffer", new StringBuilder());
272 
273         assertNotNull(Subclass.getBuffer());
274     }
275 
276     @Test
277     public void attemptToSetStaticFieldByNameWithWrongName() {
278         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
279             Deencapsulation.setField(Subclass.class, "noField", null);
280         });
281         assertEquals("No static field of name \"noField\" found in class otherTests.BaseClass", throwable.getMessage());
282     }
283 
284     @Test
285     void setStaticFieldByType() {
286         Subclass.setBuffer(null);
287 
288         Deencapsulation.setField(Subclass.class, new StringBuilder());
289 
290         assertNotNull(Subclass.getBuffer());
291     }
292 
293     @Test
294     public void attemptToSetFieldByTypeWithoutAValue() {
295         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
296             Deencapsulation.setField(Subclass.class, null);
297         });
298         assertEquals("Missing field value when setting field by type", throwable.getMessage());
299     }
300 
301     @Test
302     public void attemptToSetStaticFieldByTypeWithWrongType() {
303         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
304             Deencapsulation.setField(Subclass.class, new String());
305         });
306         assertEquals("Static field of type String not found in class otherTests.BaseClass", throwable.getMessage());
307     }
308 
309     @Test
310     public void attemptToSetStaticFieldByTypeForClassWithMultipleFieldsOfThatType() {
311         Throwable throwable = assertThrows(IllegalArgumentException.class, () -> {
312             Deencapsulation.setField(Subclass.class, 'A');
313         });
314         assertEquals("More than one static field to which a value of type char or "
315                 + "Character can be assigned exists in class "
316                 + "otherTests.DeencapsulationTest$Subclass: static1, static2", throwable.getMessage());
317     }
318 
319     @Test
320     void setFinalInstanceFields() {
321         Subclass obj = new Subclass();
322 
323         Deencapsulation.setField(obj, "INITIAL_VALUE", 123);
324         Deencapsulation.setField(obj, "initialValue", 123);
325 
326         assertEquals(123, obj.INITIAL_VALUE);
327         assertEquals(123, (int) Deencapsulation.getField(obj, "initialValue"));
328         assertEquals(-1, obj.initialValue); // in this case, the compile-time constant gets embedded in client code
329     }
330 
331     @Test
332     public void attemptToSetAStaticFinalField() {
333         Throwable throwable = assertThrows(RuntimeException.class, () -> {
334             Deencapsulation.setField(Subclass.class, "constantField", 54);
335         });
336         assertEquals(
337                 "java.lang.IllegalAccessException: Can not set static final java.lang.Integer "
338                         + "field otherTests.DeencapsulationTest$Subclass.constantField to java.lang.Integer",
339                 throwable.getMessage());
340     }
341 
342     @Test
343     void invokeInstanceMethodWithExplicitParameterTypes() {
344         String result = Deencapsulation.invoke(anInstance, "privateMethod", new Class<?>[] { int.class }, 42);
345 
346         assertEquals("result-42", result);
347     }
348 
349     @Test
350     void invokeInstanceMethodWithNonNullArgs() {
351         String result = Deencapsulation.invoke(anInstance, "privateMethod", 7);
352 
353         assertEquals("result-7", result);
354     }
355 
356     @Test
357     void invokeStaticMethodWithExplicitParameterTypes() {
358         String result = Deencapsulation.invoke(Subclass.class, "privateStaticMethod", new Class<?>[] { String.class },
359                 "hello");
360 
361         assertEquals("static-hello", result);
362     }
363 
364     @Test
365     void invokeStaticMethodWithNonNullArgs() {
366         String result = Deencapsulation.invoke(Subclass.class, "privateStaticMethod", "world");
367 
368         assertEquals("static-world", result);
369     }
370 
371     @Test
372     void invokeStaticMethodByClassName() {
373         String result = Deencapsulation.invoke("otherTests.DeencapsulationTest$Subclass", "privateStaticMethod",
374                 "byName");
375 
376         assertEquals("static-byName", result);
377     }
378 }