View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.beanutils2;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertInstanceOf;
21  import static org.junit.jupiter.api.Assertions.assertNotNull;
22  import static org.junit.jupiter.api.Assertions.assertNull;
23  import static org.junit.jupiter.api.Assertions.assertThrows;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.lang.reflect.Method;
27  import java.lang.reflect.Modifier;
28  
29  import org.apache.commons.beanutils2.priv.PrivateBeanFactory;
30  import org.apache.commons.beanutils2.priv.PublicSubBean;
31  import org.junit.jupiter.api.AfterEach;
32  import org.junit.jupiter.api.BeforeEach;
33  import org.junit.jupiter.api.Test;
34  
35  /**
36   * <p>
37   * Test case for {@code MethodUtils}
38   * </p>
39   */
40  public class MethodUtilsTest {
41  
42      private static void assertMethod(final Method method, final String methodName) {
43          assertNotNull(method);
44          assertEquals(methodName, method.getName(), "Method is not named correctly");
45          assertTrue(Modifier.isPublic(method.getModifiers()), "Method is not public");
46      }
47  
48      /**
49       * Sets up instance variables required by this test case.
50       */
51      @BeforeEach
52      public void setUp() {
53      }
54  
55      /**
56       * Tear down instance variables required by this test case.
57       */
58      @AfterEach
59      public void tearDown() {
60      }
61  
62      /**
63       * Test {@link MethodUtils#clearCache()}.
64       */
65      @Test
66      public void testClearCache() throws Exception {
67          MethodUtils.clearCache(); // make sure it starts empty
68          final PublicSubBean bean = new PublicSubBean();
69          MethodUtils.invokeMethod(bean, "setFoo", "alpha");
70          assertEquals(1, MethodUtils.clearCache());
71          assertEquals(0, MethodUtils.clearCache());
72      }
73  
74      /**
75       * <p>
76       * Test {@code getAccessibleMethod}.
77       */
78      @Test
79      public void testGetAccessibleMethod() {
80          // easy bit first - find a public method
81          final Method method = MethodUtils.getAccessibleMethod(TestBean.class, "setStringProperty", String.class);
82  
83          assertMethod(method, "setStringProperty");
84      }
85  
86      @Test
87      public void testGetAccessibleMethodFromInterface() {
88          Method method;
89          // trickier this one - find a method in a direct interface
90          method = MethodUtils.getAccessibleMethod(PrivateBeanFactory.create().getClass(), "methodBar", String.class);
91  
92          assertMethod(method, "methodBar");
93      }
94  
95      @Test
96      public void testGetAccessibleMethodIndirectInterface() {
97          Method method;
98          // trickier this one - find a method in a indirect interface
99          method = MethodUtils.getAccessibleMethod(PrivateBeanFactory.createSubclass().getClass(), "methodBaz", String.class);
100 
101         assertMethod(method, "methodBaz");
102     }
103 
104     /**
105      * <p>
106      * Test {@code invokeExactMethod}.
107      */
108     @Test
109     public void testInvokeExactMethod() throws Exception {
110         final TestBean bean = new TestBean();
111         final Object ret = MethodUtils.invokeExactMethod(bean, "setStringProperty", "TEST");
112 
113         assertNull(ret);
114         assertEquals("TEST", bean.getStringProperty(), "Method ONE was invoked");
115     }
116 
117     @Test
118     public void testInvokeExactMethodFromInterface() throws Exception {
119         final Object ret = MethodUtils.invokeExactMethod(PrivateBeanFactory.create(), "methodBar", "ANOTHER TEST");
120 
121         assertEquals("ANOTHER TEST", ret, "Method TWO wasn't invoked correctly");
122     }
123 
124     @Test
125     public void testInvokeExactMethodIndirectInterface() throws Exception {
126         final Object ret = MethodUtils.invokeExactMethod(PrivateBeanFactory.createSubclass(), "methodBaz", "YET ANOTHER TEST");
127 
128         assertEquals("YET ANOTHER TEST", ret, "Method TWO was invoked correctly");
129     }
130 
131     @Test
132     public void testInvokeExactMethodNull() throws Exception {
133         final Object object = new Object();
134         final Object result = MethodUtils.invokeExactMethod(object, "toString", (Object) null);
135         assertEquals(object.toString(), result);
136     }
137 
138     @Test
139     public void testInvokeExactMethodNullArray() throws Exception {
140         final Object result = MethodUtils.invokeExactMethod(new AlphaBean("parent"), "getName", null);
141         assertEquals("parent", result);
142     }
143 
144     @Test
145     public void testInvokeExactMethodNullArrayNullArray() throws Exception {
146         final Object result = MethodUtils.invokeExactMethod(new AlphaBean("parent"), "getName", null, null);
147 
148         assertEquals("parent", result);
149     }
150 
151     @Test
152     public void testInvokeExactStaticMethodNull() throws Exception {
153         final int current = TestBean.currentCounter();
154         final Object value = MethodUtils.invokeExactStaticMethod(TestBean.class, "currentCounter", (Object) null);
155         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
156     }
157 
158     /**
159      * <p>
160      * Test {@code invokeMethod}.
161      */
162     @Test
163     public void testInvokeMethod() throws Exception {
164         final AbstractParent parent = new AlphaBean("parent");
165         final BetaBean childOne = new BetaBean("ChildOne");
166 
167         assertEquals("ChildOne", MethodUtils.invokeMethod(parent, "testAddChild", childOne), "Cannot invoke through abstract class (1)");
168     }
169 
170     @Test
171     public void testInvokeMethodArray() throws Exception {
172         final AbstractParent parent = new AlphaBean("parent");
173         final AlphaBean childTwo = new AlphaBean("ChildTwo");
174 
175         final Object[] params = new Object[2];
176         params[0] = "parameter";
177         params[1] = childTwo;
178 
179         assertEquals("ChildTwo", MethodUtils.invokeMethod(parent, "testAddChild2", params), "Cannot invoke through abstract class");
180     }
181 
182     @Test
183     public void testInvokeMethodNull() throws Exception {
184         final Object object = new Object();
185         final Object result = MethodUtils.invokeMethod(object, "toString", (Object) null);
186         assertEquals(object.toString(), result);
187     }
188 
189     @Test
190     public void testInvokeMethodNullArray() throws Exception {
191         final Object result = MethodUtils.invokeMethod(new AlphaBean("parent"), "getName", null);
192 
193         assertEquals("parent", result);
194     }
195 
196     @Test
197     public void testInvokeMethodNullArrayNullArray() throws Exception {
198         final Object result = MethodUtils.invokeMethod(new AlphaBean("parent"), "getName", null, null);
199 
200         assertEquals("parent", result);
201     }
202 
203     @Test
204     public void testInvokeMethodObject() throws Exception {
205         final AbstractParent parent = new AlphaBean("parent");
206         final Child childTwo = new AlphaBean("ChildTwo");
207 
208         assertEquals("ChildTwo", MethodUtils.invokeMethod(parent, "testAddChild", childTwo), "Cannot invoke through interface (1)");
209     }
210 
211     @Test
212     public void testInvokeMethodPrimitiveBoolean() throws Exception {
213         final PrimitiveBean bean = new PrimitiveBean();
214         MethodUtils.invokeMethod(bean, "setBoolean", Boolean.FALSE);
215         assertEquals(false, bean.getBoolean(), "Call boolean property using invokeMethod");
216     }
217 
218     @Test
219     public void testInvokeMethodPrimitiveDouble() throws Exception {
220         final PrimitiveBean bean = new PrimitiveBean();
221         MethodUtils.invokeMethod(bean, "setDouble", Double.valueOf(25.5d));
222         assertEquals(25.5d, bean.getDouble(), 0.01d, "Set double property using invokeMethod");
223     }
224 
225     @Test
226     public void testInvokeMethodPrimitiveFloat() throws Exception {
227         final PrimitiveBean bean = new PrimitiveBean();
228         MethodUtils.invokeMethod(bean, "setFloat", Float.valueOf(20.0f));
229         assertEquals(20.0f, bean.getFloat(), 0.01f, "Call float property using invokeMethod");
230     }
231 
232     @Test
233     public void testInvokeMethodPrimitiveInt() throws Exception {
234         final PrimitiveBean bean = new PrimitiveBean();
235         MethodUtils.invokeMethod(bean, "setInt", Integer.valueOf(12));
236         assertEquals(12, bean.getInt(), "Set int property using invokeMethod");
237     }
238 
239     @Test
240     public void testInvokeMethodPrimitiveLong() throws Exception {
241         final PrimitiveBean bean = new PrimitiveBean();
242         MethodUtils.invokeMethod(bean, "setLong", Long.valueOf(10));
243         assertEquals(10, bean.getLong(), "Call long property using invokeMethod");
244     }
245 
246     @Test
247     public void testInvokeMethodUnknown() throws Exception {
248         // test that exception is correctly thrown when a method cannot be found with matching params
249         final AbstractParent parent = new AlphaBean("parent");
250         final BetaBean childOne = new BetaBean("ChildOne");
251         assertThrows(NoSuchMethodException.class, () -> MethodUtils.invokeMethod(parent, "bogus", childOne));
252     }
253 
254     @Test
255     public void testInvokeStaticMethodNull() throws Exception {
256         final int current = TestBean.currentCounter();
257         final Object value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", (Object) null);
258         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
259     }
260 
261     @Test
262     public void testNoCaching() throws Exception {
263         // no caching
264         MethodUtils.setCacheMethods(false);
265 
266         final PublicSubBean bean = new PublicSubBean();
267         MethodUtils.invokeMethod(bean, "setFoo", "alpha");
268         assertEquals(0, MethodUtils.clearCache());
269 
270         // reset default
271         MethodUtils.setCacheMethods(true);
272     }
273 
274     @Test
275     public void testParentMethod() throws Exception {
276         final String a = "A";
277         final String actual1 = (String) MethodUtils.invokeMethod(a, "toLowerCase", null);
278         assertEquals("a", actual1);
279         final char actual2 = (char) MethodUtils.invokeMethod(a, "charAt", 0);
280         assertEquals('A', actual2);
281     }
282 
283     @Test
284     public void testPublicSub() throws Exception {
285         // make sure that bean does what it should
286         final PublicSubBean bean = new PublicSubBean();
287         assertEquals(bean.getFoo(), "This is foo", "Start value (foo)");
288         assertEquals(bean.getBar(), "This is bar", "Start value (bar)");
289         bean.setFoo("new foo");
290         bean.setBar("new bar");
291         assertEquals(bean.getFoo(), "new foo", "Set value (foo)");
292         assertEquals(bean.getBar(), "new bar", "Set value (bar)");
293 
294         // see if we can access public methods in a default access superclass
295         // from a public access subclass instance
296         MethodUtils.invokeMethod(bean, "setFoo", "alpha");
297         assertEquals(bean.getFoo(), "alpha", "Set value (foo:2)");
298         MethodUtils.invokeMethod(bean, "setBar", "beta");
299         assertEquals(bean.getBar(), "beta", "Set value (bar:2)");
300 
301         Method method = MethodUtils.getAccessibleMethod(PublicSubBean.class, "setFoo", String.class);
302         assertNotNull(method, "getAccessibleMethod() setFoo is Null");
303         method.invoke(bean, "1111");
304         assertEquals("1111", bean.getFoo(), "Set value (foo:3)");
305 
306         method = MethodUtils.getAccessibleMethod(PublicSubBean.class, "setBar", String.class);
307         assertNotNull(method, "getAccessibleMethod() setBar is Null");
308         method.invoke(bean, "2222");
309         assertEquals("2222", bean.getBar(), "Set value (bar:3)");
310 
311     }
312 
313     /**
314      * Test {@link MethodUtils#setCacheMethods(boolean)}.
315      */
316     @Test
317     public void testSetCacheMethods() throws Exception {
318         MethodUtils.setCacheMethods(true);
319         MethodUtils.clearCache(); // make sure it starts empty
320 
321         final PublicSubBean bean = new PublicSubBean();
322         MethodUtils.invokeMethod(bean, "setFoo", "alpha");
323         assertEquals(1, MethodUtils.clearCache());
324         assertEquals(0, MethodUtils.clearCache());
325     }
326 
327     /**
328      * Simple tests for accessing static methods via invokeMethod().
329      */
330     @Test
331     public void testSimpleStatic1() throws Exception {
332         final TestBean bean = new TestBean();
333         Object value = null;
334         int current = TestBean.currentCounter();
335         // Return initial value of the counter
336         value = MethodUtils.invokeMethod(bean, "currentCounter", new Object[0], new Class[0]);
337         assertNotNull(value, "currentCounter exists");
338         assertInstanceOf(Integer.class, value, "currentCounter type");
339         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
340 
341         // Increment via no-arguments version
342         MethodUtils.invokeMethod(bean, "incrementCounter", new Object[0], new Class[0]);
343 
344         // Validate updated value
345         current++;
346         value = MethodUtils.invokeMethod(bean, "currentCounter", new Object[0], new Class[0]);
347         assertNotNull(value, "currentCounter exists");
348         assertInstanceOf(Integer.class, value, "currentCounter type");
349         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
350 
351         // Increment via specified-argument version
352         MethodUtils.invokeMethod(bean, "incrementCounter", new Object[] { Integer.valueOf(5) }, new Class[] { Integer.TYPE });
353 
354         // Validate updated value
355         current += 5;
356         value = MethodUtils.invokeMethod(bean, "currentCounter", new Object[0], new Class[0]);
357         assertNotNull(value, "currentCounter exists");
358         assertInstanceOf(Integer.class, value, "currentCounter type");
359         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
360     }
361 
362     /**
363      * Simple tests for accessing static methods via invokeExactMethod().
364      */
365     @Test
366     public void testSimpleStatic2() throws Exception {
367         final TestBean bean = new TestBean();
368         Object value = null;
369         int current = TestBean.currentCounter();
370         // Return initial value of the counter
371         value = MethodUtils.invokeExactMethod(bean, "currentCounter", new Object[0], new Class[0]);
372         assertNotNull(value, "currentCounter exists");
373         assertInstanceOf(Integer.class, value, "currentCounter type");
374         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
375 
376         // Increment via no-arguments version
377         MethodUtils.invokeExactMethod(bean, "incrementCounter", new Object[0], new Class[0]);
378 
379         // Validate updated value
380         current++;
381         value = MethodUtils.invokeExactMethod(bean, "currentCounter", new Object[0], new Class[0]);
382         assertNotNull(value, "currentCounter exists");
383         assertInstanceOf(Integer.class, value, "currentCounter type");
384         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
385 
386         // Increment via specified-argument version
387         MethodUtils.invokeExactMethod(bean, "incrementCounter", new Object[] { Integer.valueOf(5) }, new Class[] { Integer.TYPE });
388 
389         // Validate updated value
390         current += 5;
391         value = MethodUtils.invokeExactMethod(bean, "currentCounter", new Object[0], new Class[0]);
392         assertNotNull(value, "currentCounter exists");
393         assertInstanceOf(Integer.class, value, "currentCounter type");
394         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
395     }
396 
397     /**
398      * Simple tests for accessing static methods via getAccessibleMethod()
399      */
400     @Test
401     public void testSimpleStatic3() throws Exception {
402         Object value = null;
403         int current = TestBean.currentCounter();
404         // Acquire the methods we need
405         final Method currentCounterMethod = MethodUtils.getAccessibleMethod(TestBean.class, "currentCounter", new Class[0]);
406         assertNotNull(currentCounterMethod, "currentCounterMethod exists");
407         assertEquals("currentCounter", currentCounterMethod.getName(), "currentCounterMethod name");
408         assertEquals(0, currentCounterMethod.getParameterTypes().length, "currentCounterMethod args");
409         assertTrue(Modifier.isPublic(currentCounterMethod.getModifiers()), "currentCounterMethod public");
410         assertTrue(Modifier.isStatic(currentCounterMethod.getModifiers()), "currentCounterMethod static");
411         final Method incrementCounterMethod1 = MethodUtils.getAccessibleMethod(TestBean.class, "incrementCounter", new Class[0]);
412         assertNotNull(incrementCounterMethod1, "incrementCounterMethod1 exists");
413         assertEquals("incrementCounter", incrementCounterMethod1.getName(), "incrementCounterMethod1 name");
414         assertEquals(0, incrementCounterMethod1.getParameterTypes().length, "incrementCounterMethod1 args");
415         assertTrue(Modifier.isPublic(incrementCounterMethod1.getModifiers()), "incrementCounterMethod1 public");
416         assertTrue(Modifier.isStatic(incrementCounterMethod1.getModifiers()), "incrementCounterMethod1 static");
417         final Method incrementCounterMethod2 = MethodUtils.getAccessibleMethod(TestBean.class, "incrementCounter", new Class[] { Integer.TYPE });
418         assertNotNull(incrementCounterMethod2, "incrementCounterMethod2 exists");
419         assertEquals("incrementCounter", incrementCounterMethod2.getName(), "incrementCounterMethod2 name");
420         assertEquals(1, incrementCounterMethod2.getParameterTypes().length, "incrementCounterMethod2 args");
421         assertTrue(Modifier.isPublic(incrementCounterMethod2.getModifiers()), "incrementCounterMethod2 public");
422         assertTrue(Modifier.isStatic(incrementCounterMethod2.getModifiers()), "incrementCounterMethod2 static");
423 
424         // Return initial value of the counter
425         value = currentCounterMethod.invoke(null);
426         assertNotNull(value, "currentCounter exists");
427         assertInstanceOf(Integer.class, value, "currentCounter type");
428         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
429 
430         // Increment via no-arguments version
431         incrementCounterMethod1.invoke(null);
432 
433         // Validate updated value
434         current++;
435         value = currentCounterMethod.invoke(null);
436         assertNotNull(value, "currentCounter exists");
437         assertInstanceOf(Integer.class, value, "currentCounter type");
438         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
439 
440         // Increment via specified-argument version
441         incrementCounterMethod2.invoke(null, Integer.valueOf(5));
442 
443         // Validate updated value
444         current += 5;
445         value = currentCounterMethod.invoke(null);
446         assertNotNull(value, "currentCounter exists");
447         assertInstanceOf(Integer.class, value, "currentCounter type");
448         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
449     }
450 
451     @Test
452     public void testStaticInvokeMethod() throws Exception {
453 
454         Object value;
455         int current = TestBean.currentCounter();
456 
457         value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", new Object[0]);
458         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
459 
460         MethodUtils.invokeStaticMethod(TestBean.class, "incrementCounter", new Object[0]);
461         current++;
462 
463         value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", new Object[0]);
464         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
465 
466         MethodUtils.invokeStaticMethod(TestBean.class, "incrementCounter", new Object[] { Integer.valueOf(8) });
467         current += 8;
468 
469         value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", new Object[0]);
470         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
471 
472         MethodUtils.invokeExactStaticMethod(TestBean.class, "incrementCounter", new Object[] { Integer.valueOf(8) }, new Class[] { Number.class });
473         current += 16;
474 
475         value = MethodUtils.invokeStaticMethod(TestBean.class, "currentCounter", new Object[0]);
476         assertEquals(current, ((Integer) value).intValue(), "currentCounter value");
477     }
478 }