Null pointer exception reflected after Spring injection
1. Exception description
In a unit test class, you need to test the private method in a class separately, so you want to get this method through reflection, release the private qualification, and then call it. This class injects many mappers into the Service, so an error of null pointer exception is reported when calling this private method. The error is as follows:
java.lang.reflect.InvocationTargetException 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.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImplTest.testInvoke(CheckAmsPmtAndPtsFlowServiceImplTest.java:78) at com.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImplTest.insertByList(CheckAmsPmtAndPtsFlowServiceImplTest.java:69) 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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) 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.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) 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) Caused by: java.lang.NullPointerException at com.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImpl.succesExitReove(CheckAmsPmtAndPtsFlowServiceImpl.java:247) ... 36 more
Note that the last is caused by NullPointerException. After debugging, it is found that Mapper interacting with database is not injected.
The unit test code is as follows:
@Autowired private CheckAmsPmtAndPtsFlowServiceImpl checkAmsPmtAndPtsFlowServiceI; @Test public void insertByList() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { List<TBapCheckResultSave> list = new ArrayList<>(); Class cls = Class.forName("service.impl.CheckAmsPmtAndPtsFlowServiceImpl"); Class[] cArg = new Class[5]; cArg[0] = List.class; cArg[1] = boolean.class; cArg[2] = String.class; cArg[3] = String.class; cArg[4] = String.class; Method succesExitReove = cls.getDeclaredMethod("succesExitReove",cArg); succesExitReove.setAccessible(true); succesExitReove.invoke(checkAmsPmtAndPtsFlowServiceI,list,true,"1","1","2"); }
Here's a knowledge point: when calling getDeclaredMethod() to get the specified method, the second parameter is the parameter passing type of the method, and the wrapper class is different from its basic type. For example, the following method is boolean. If it's written as its wrapper type at this time, then it will report an error that the method can't be found
The method succesexititreove() in the reflected class is as follows
@Autowired private TMemAcPmtDtlPoMapper tMemAcPmtDtlPoMapper; private void succesExitReove(List<TBapCheckResultSave> result, boolean isFen, String sourceType, String opId, String batchId) { Iterator<TBapCheckResultSave> iteDownPtsNoCodeSuccces = result.iterator(); while (iteDownPtsNoCodeSuccces.hasNext()) { int count; if (isFen) { count = tMemAcPmtDtlPoMapper.queryCheckFlow(bapCheckResult.getOutTransId()); } else { count = tMemAcPmtDtlPoMapper.queryCheckFlow(bapCheckResult.getTransId()); } bapCheckResult.setPmtCount(count); if (count > 0) { iteDownPtsNoCodeSuccces.remove(); } } }
It is found that tmemaacpmtdtlpompper is empty during debugging.
2. Solutions
In the unit test class, the Mapper to be used by @ Autowired is obtained through reflection, and the variables of Mapper to be used in the above methods are assigned
The unit test code is as follows
@Autowired private CheckAmsPmtAndPtsFlowServiceImpl checkAmsPmtAndPtsFlowServiceI; --Dependency injection in unit test classes Mapper @Autowired private TMemAcPmtDtlPoMapper tMemAcPmtDtlPoMapper; @Test public void insertByList() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { List<TBapCheckResultSave> list = new ArrayList<>(); Class cls = Class.forName("service.impl.CheckAmsPmtAndPtsFlowServiceImpl"); Class[] cArg = new Class[5]; cArg[0] = List.class; cArg[1] = boolean.class; cArg[2] = String.class; cArg[3] = String.class; cArg[4] = String.class; Field a1 = cls.getDeclaredField("tMemAcPmtDtlPoMapper"); Method succesExitReove = cls.getDeclaredMethod("succesExitReove",cArg); succesExitReove.setAccessible(true); --Assign a value here to the reflected class Mapper Field a1 = cls.getDeclaredField("tMemAcPmtDtlPoMapper"); a1.setAccessible(true); a1.set(checkAmsPmtAndPtsFlowServiceI,tMemAcPmtDtlPoMapper); succesExitReove.invoke(checkAmsPmtAndPtsFlowServiceI,list,true,"1","1","2"); }