我正在使用PowerMock和Mockito测试弹簧控制器。
我已定义了类TestController(请参见下文,狙击手#1),并且为其定义了单元测试(请参见下文,狙击手#2)。但是,当我尝试进行单元测试时,我会得到一个例外(请参见下文,狙击手#3)。
如果我删除了@injectmocks,请在定义上删除testController实例,然后在测试功能中删除contrancterut = new TestController(),它可以正常工作(请参见下文,Snipper#4)。
这使我相信在@InjectMocks之前不会发生静态替换,我的问题是,这是事情的工作方式,还是我做错了什么?是否有更好的方法设计代码以避免此问题?我猜想人们使用静态日志分配(我没有发明此分配),因此有人必须在此问题上遇到这个问题...
谢谢!
摘要#1
@Controller
@RequestMapping("/api/test")
public class TestController {
private static final Logger LOG = LoggerFactory.getLogger(TestController.class);
@Autowired
private GeneralService generalService;
@RequestMapping(method=RequestMethod.GET)
public void doSomethingUseful(
HttpServletRequest request,
HttpServletResponse response) {
// nothing userful to do right now
}
}
摘要#2
@RunWith(PowerMockRunner.class)
@PrepareForTest({TestController.class, LoggerFactory.class})
public class TestControllerTest {
@InjectMocks
private TestController controllerUT = new TestController();
@Mock
private GeneralService service;
@Mock
private Logger loggerMock;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Before
public void setUp() {
PowerMockito.mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).
thenReturn(loggerMock);
}
@Test
public void doSomethingUsefulTest() {
controllerUT.doSomethingUseful(request, response);
assert(true);
}
}
摘要#3
Failed to auto configure default logger context
Reported exception:
ch.qos.logback.core.joran.spi.JoranException: Parser configuration error occurred
at ch.qos.logback.core.joran.event.SaxEventRecorder.buildSaxParser(SaxEventRecorder.java:86)
at ch.qos.logback.core.joran.event.SaxEventRecorder.recordEvents(SaxEventRecorder.java:57)
at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:132)
at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:96)
at ch.qos.logback.core.joran.GenericConfigurator.doConfigure(GenericConfigurator.java:55)
at ch.qos.logback.classic.util.ContextInitializer.configureByResource(ContextInitializer.java:75)
at ch.qos.logback.classic.util.ContextInitializer.autoConfig(ContextInitializer.java:148)
at org.slf4j.impl.StaticLoggerBinder.init(StaticLoggerBinder.java:84)
at org.slf4j.impl.StaticLoggerBinder.<clinit>(StaticLoggerBinder.java:54)
at org.slf4j.LoggerFactory.bind(LoggerFactory.java:128)
at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:108)
at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:279)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:252)
at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:265)
at com.basicservice.controller.TestController.<clinit>(TestController.java:39)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at javassist.runtime.Desc.getClassObject(Desc.java:43)
at javassist.runtime.Desc.getClassType(Desc.java:152)
at javassist.runtime.Desc.getType(Desc.java:122)
at javassist.runtime.Desc.getType(Desc.java:78)
at com.basicservice.controller.TestControllerTest.<init>(TestControllerTest.java:44)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.createTestInstance(PowerMockJUnit44RunnerDelegateImpl.java:188)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.createTest(PowerMockJUnit44RunnerDelegateImpl.java:173)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:195)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:148)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:102)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:42)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.ClassCastException: org.apache.xerces.jaxp.SAXParserFactoryImpl cannot be cast to javax.xml.parsers.SAXParserFactory
at javax.xml.parsers.SAXParserFactory.newInstance(SAXParserFactory.java:128)
at ch.qos.logback.core.joran.event.SaxEventRecorder.buildSaxParser(SaxEventRecorder.java:79)
... 42 more
摘要#4
@RunWith(PowerMockRunner.class)
@PrepareForTest({TestController.class, LoggerFactory.class})
public class TestControllerTest {
private TestController controllerUT;
@Mock
private GeneralService service;
@Mock
private Logger loggerMock;
@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Before
public void setUp() {
PowerMockito.mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).
thenReturn(loggerMock);
}
@Test
public void doSomethingUsefulTest() {
controllerUT = new TestController();
controllerUT.doSomethingUseful(request, response);
assert(true);
}
}
需要重新记录的一件事是 @InjectMocks
尊重静态和最终字段,即不会在静态或最终字段中注入模拟。另请注意,PowerMock必须产生一个新的class Loader才能"仪器"类,这可能解释了片段#3。
当我没有探索您的项目的来龙去脉时,我相信如果您想使用PowerMock,您可能需要准备所有的logback/slf4j类。仍然请记住,PowerMock和Mockito是不同的项目,并且可能不会像人们想象的那样一起工作。