Java 在嵌入式Tomcat中使用PowerMock
我试图模拟一个由嵌入式Tomcat实例调用的静态方法。这是我的测试课:Java 在嵌入式Tomcat中使用PowerMock,java,unit-testing,tomcat,powermock,powermockito,Java,Unit Testing,Tomcat,Powermock,Powermockito,我试图模拟一个由嵌入式Tomcat实例调用的静态方法。这是我的测试课: @RunWith(PowerMockRunner.class) @PrepareForTest(ExternalAPI.class) @PowerMockIgnore({"javax.management.*"}) public class TestExternalAPI { @Before public void setup() { Tomcat tomcat = new Tomcat()
@RunWith(PowerMockRunner.class)
@PrepareForTest(ExternalAPI.class)
@PowerMockIgnore({"javax.management.*"})
public class TestExternalAPI
{
@Before
public void setup() {
Tomcat tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.enableNaming();
tomcat.addWebapp("/app", new File("src/test/webapp").getAbsolutePath());
tomcat.start();
}
@Test
public void testAPI() {
mockStatic(ExternalAPI.class);
when(ExternalAPI.getData()).thenReturn(new Data());
//call Tomcat triggering the call to ExternalAPI.getData()
}
}
通过此配置,我得到以下异常:
java.lang.NoSuchMethodException: org.apache.catalina.deploy.WebXml addServlet
at org.apache.tomcat.util.IntrospectionUtils.callMethod1(IntrospectionUtils.java:849)
at org.apache.tomcat.util.digester.SetNextRule.end(SetNextRule.java:201)
at org.apache.tomcat.util.digester.Digester.endElement(Digester.java:1060)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:609)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1783)
...
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl cannot be cast to javax.xml.parsers.SAXParserFactory
at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:190)
... 54 more
现在我想也许我需要告诉PowerMock忽略org.apache.*包,但是我得到了这个例外:
java.lang.NoSuchMethodException: org.apache.catalina.deploy.WebXml addServlet
at org.apache.tomcat.util.IntrospectionUtils.callMethod1(IntrospectionUtils.java:849)
at org.apache.tomcat.util.digester.SetNextRule.end(SetNextRule.java:201)
at org.apache.tomcat.util.digester.Digester.endElement(Digester.java:1060)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:609)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1783)
...
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl cannot be cast to javax.xml.parsers.SAXParserFactory
at javax.xml.parsers.FactoryFinder.newInstance(FactoryFinder.java:190)
... 54 more
我还尝试忽略所有org.*包,这是可行的,但是ExternalAPI.getData()方法根本没有被模仿。我想之所以会发生这种情况,是因为在本例中没有模拟来自Tomcat实例的任何内容,因为我们忽略了那些org.apache.*包
为什么会出现这些异常?我应该如何配置PowerMock
编辑:我正在使用Tomcat7。如果未使用PowerMock,则使用嵌入式Tomcat实例测试运行良好。我认为在嵌入式模式下使用Tomcat时,应该设置所有必需的内容,如协议、生命周期侦听器(如果有)、servlet类、上下文参数 为我工作的示例代码
tomcat = new Tomcat();
// Trigger loading of catalina.properties
CatalinaProperties.getProperty("foo");
File appBase = new File(getTemporaryDirectory(), "webapps");
if (!appBase.exists() && !appBase.mkdir()) {
Assert.fail("Unable to create appBase for test");
}
String protocol = getProtocol();
Connector connector = new Connector(protocol);
// Listen only on localhost
connector.setAttribute("address",
InetAddress.getByName("localhost").getHostAddress());
// Use random free port
connector.setPort(0);
// Mainly set to reduce timeouts during async tests
connector.setAttribute("connectionTimeout", "3000");
tomcat.getService().addConnector(connector);
tomcat.setConnector(connector);
// Add AprLifecycleListener if we are using the Apr connector
if (protocol.contains("Apr")) {
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
listener.setSSLRandomSeed("/dev/urandom");
server.addLifecycleListener(listener);
connector.setAttribute("pollerThreadCount", Integer.valueOf(1));
}
File catalinaBase = getTemporaryDirectory();
tomcat.setBaseDir(catalinaBase.getAbsolutePath());
tomcat.getHost().setAppBase(appBase.getAbsolutePath());
System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
Context ctx = tomcat.addContext(getContext(), appBase.getAbsolutePath());
ctx.setParentClassLoader(EmbeddedTomcatTestBase.class.getClassLoader());
//Set execution independent of current thread context classloader (compatibility with exec:java mojo)
Wrapper w = Tomcat.addServlet(ctx, "ServletName", ServletClass.class.getName());
w.setLoadOnStartup(1);
w.addMapping("/ServletURI/*");
w.addMapping("/ServletURI");
w.addInitParameter("StatusPage", "/diagnostic/status.jsp");
Wrapper w1 = Tomcat.addServlet(ctx, "statusJSP", JspServlet.class.getName());
w1.addMapping("/diagnostic/status.jsp");
Wrapper w3 = Tomcat.addServlet(ctx, "errorJSP", JspServlet.class.getName());
w3.addMapping("/diagnostic/error.jsp");
ctx.addParameter("EmbeddedMode", "true");
ctx.addApplicationListener(ServletContextListnereClass.class.getName());
ContextConfig contextConfig = new ContextConfig();
ctx.addLifecycleListener(contextConfig);
Tomcat.initWebappDefaults(ctx);
tomcat.start();
// initialize your engine / invoke any java methods of your interest here..
我认为在嵌入式模式下使用tomcat时,应该设置所有必需的内容,比如协议、生命周期侦听器(如果有)、servlet类、上下文参数 为我工作的示例代码
tomcat = new Tomcat();
// Trigger loading of catalina.properties
CatalinaProperties.getProperty("foo");
File appBase = new File(getTemporaryDirectory(), "webapps");
if (!appBase.exists() && !appBase.mkdir()) {
Assert.fail("Unable to create appBase for test");
}
String protocol = getProtocol();
Connector connector = new Connector(protocol);
// Listen only on localhost
connector.setAttribute("address",
InetAddress.getByName("localhost").getHostAddress());
// Use random free port
connector.setPort(0);
// Mainly set to reduce timeouts during async tests
connector.setAttribute("connectionTimeout", "3000");
tomcat.getService().addConnector(connector);
tomcat.setConnector(connector);
// Add AprLifecycleListener if we are using the Apr connector
if (protocol.contains("Apr")) {
StandardServer server = (StandardServer) tomcat.getServer();
AprLifecycleListener listener = new AprLifecycleListener();
listener.setSSLRandomSeed("/dev/urandom");
server.addLifecycleListener(listener);
connector.setAttribute("pollerThreadCount", Integer.valueOf(1));
}
File catalinaBase = getTemporaryDirectory();
tomcat.setBaseDir(catalinaBase.getAbsolutePath());
tomcat.getHost().setAppBase(appBase.getAbsolutePath());
System.setProperty("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE", "true");
Context ctx = tomcat.addContext(getContext(), appBase.getAbsolutePath());
ctx.setParentClassLoader(EmbeddedTomcatTestBase.class.getClassLoader());
//Set execution independent of current thread context classloader (compatibility with exec:java mojo)
Wrapper w = Tomcat.addServlet(ctx, "ServletName", ServletClass.class.getName());
w.setLoadOnStartup(1);
w.addMapping("/ServletURI/*");
w.addMapping("/ServletURI");
w.addInitParameter("StatusPage", "/diagnostic/status.jsp");
Wrapper w1 = Tomcat.addServlet(ctx, "statusJSP", JspServlet.class.getName());
w1.addMapping("/diagnostic/status.jsp");
Wrapper w3 = Tomcat.addServlet(ctx, "errorJSP", JspServlet.class.getName());
w3.addMapping("/diagnostic/error.jsp");
ctx.addParameter("EmbeddedMode", "true");
ctx.addApplicationListener(ServletContextListnereClass.class.getName());
ContextConfig contextConfig = new ContextConfig();
ctx.addLifecycleListener(contextConfig);
Tomcat.initWebappDefaults(ctx);
tomcat.start();
// initialize your engine / invoke any java methods of your interest here..
谢谢你的例子,但是嵌入式Tomcat对我来说工作得很好。这个问题只有在我尝试使用PowerMock时才会出现。谢谢你的例子,但是嵌入式Tomcat对我来说工作得很好。只有当我尝试使用PowerMock时,问题才会出现。我不确定我是否理解您在这里真正想要实现的目标。通常在单元测试中,您将模拟代码所依赖的依赖项,然后调用代码并确保它正确使用模拟。除非你写的是Tomcat,否则我不知道你为什么要嵌入Tomcat而不是嘲笑它。您在最下面描述的场景是集成测试的完美示例,您也应该这样做。但是我在这里没有单元测试场景。@dcsohl我想测试这个web应用程序的整个流程。但是,我希望模拟在该流期间触发的外部调用,以便在运行测试时不需要外部系统。我试图通过在嵌入式Tomcat实例上部署应用程序,然后向其发送REST调用来实现这一点。这都是遗留应用程序的一部分,该应用程序已经有一些使用这种方法的测试,因此完全重构它们并不是一个真正的选项。我不确定我是否理解您在这里实际要完成的任务。通常在单元测试中,您将模拟代码所依赖的依赖项,然后调用代码并确保它正确使用模拟。除非你写的是Tomcat,否则我不知道你为什么要嵌入Tomcat而不是嘲笑它。您在最下面描述的场景是集成测试的完美示例,您也应该这样做。但是我在这里没有单元测试场景。@dcsohl我想测试这个web应用程序的整个流程。但是,我希望模拟在该流期间触发的外部调用,以便在运行测试时不需要外部系统。我试图通过在嵌入式Tomcat实例上部署应用程序,然后向其发送REST调用来实现这一点。这都是遗留应用程序的一部分,该应用程序已经有一些使用这种方法的测试,因此完全重构它们并不是一个真正的选项。