Background
There is a private downloadFile method of ZhangyoobaoLeshuaMerchantRegister class, the method signature is like following
1 |
private ActionResult downloadFile(String url){ |
A unit test case for this method need be created, in this test case we need pass a null value as parameter to test whether this method can handle null parameter. But a private method cannot be accessed from the instance level. The solution is to use reflection to get the method, and set its accessiblity to public temporarily (by using the Method.setAccessible method).
First let's see some code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@Test public void downloadFile() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { ChannelSecurityInfo securityInfo = new ChannelSecurityInfo(); securityInfo.setMerchantNo("6124"); securityInfo.setSecretKey("64c881c310cd42f1"); ZhangyoobaoLeshuaMerchantRegister register = new ZhangyoobaoLeshuaMerchantRegister(securityInfo); Method method = register.getClass().getDeclaredMethod("downloadFile", String.class); method.setAccessible(true); Object result = method.invoke(register, "bing.com"); System.out.println(result); result = method.invoke(register, null); System.out.println(result); } |
Above Java code is the unit test case, it uses reflection to get the private method downloadFile of class ZhangyoobaoLeshuaMerchantRegister, and set the access level of this method to public, then call it with parameters. (Note that if setAccessible is not called to set method access level, Method.invoke will throw IllegalAccessException.)
But running this line result = method.invoke(register, null); will got following error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.jh.paymentchannel.channel.zhangyoobao.ZhangyoobaoLeshuaMerchantRegisterTest.downloadFile(ZhangyoobaoLeshuaMerchantRegisterTest.java:40) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68) at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70) |
It's because the second argument of Method.invoke() is Object... (An Object[] array), its elements are arguments passed in to the method. Calling Method.invoke() with null as second argument is actually trying to call the method with no arguments, but downloadFile method requires one parameter, so it definitely raises IllegalArgumentException.
Solution
To fix this issue, change following line
1 |
result = method.invoke(register, null); |
to
1 |
result = method.invoke(register, new Object[]{null}); |
This will pass null as first argument for downloadFile method. new Object[]{null} is correspondding to the Object... argument of Method.invoke(), it's expanded like calling downloadFile(null) .