在Tomcat中运行Cucumber-TestNG的Servlet时出现空指针异常求助
我尝试在Tomcat服务器中运行一个Servlet来触发Cucumber测试,Servlet代码如下:
package com.revature.http; import java.io.File; import java.io.IOException; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.testng.ITestNGListener; import org.testng.TestListenerAdapter; import org.testng.TestNG; import org.testng.collections.Lists; public class RunCucumber extends HttpServlet { private static final long serialVersionUID = 1L; public RunCucumber() { super(); // TODO Auto-generated constructor stub } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { TestListenerAdapter adapter = new TestListenerAdapter(); TestNG testng = new TestNG(); testng.setTestClasses(new Class[] { com.revature.cucumber.CucumberRunner.class }); testng.addListener((ITestNGListener) adapter); testng.run(); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // TODO Auto-generated method stub doGet(request, response); } }
但每次调用这个Servlet时,都会抛出如下空指针异常:
[Utils] [ERROR] [Error] java.lang.NullPointerException at cucumber.api.testng.AbstractTestNGCucumberTests.features(AbstractTestNGCucumberTests.java:29) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:108) at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:55) at org.testng.internal.MethodInvocationHelper.invokeMethodNoCheckedException(MethodInvocationHelper.java:45) at org.testng.internal.MethodInvocationHelper.invokeDataProvider(MethodInvocationHelper.java:115) at org.testng.internal.Parameters.handleParameters(Parameters.java:509) at org.testng.internal.Invoker.handleParameters(Invoker.java:1308) at org.testng.internal.Invoker.createParameters(Invoker.java:1036) at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1126) at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:126) at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:109) at org.testng.TestRunner.privateRun(TestRunner.java:744) at org.testng.TestRunner.run(TestRunner.java:602) at org.testng.SuiteRunner.runTest(SuiteRunner.java:380) at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:375) at org.testng.SuiteRunner.privateRun(SuiteRunner.java:340) at org.testng.SuiteRunner.run(SuiteRunner.java:289) at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52) at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86) at org.testng.TestNG.runSuitesSequentially(TestNG.java:1301) at org.testng.TestNG.runSuitesLocally(TestNG.java:1226) at org.testng.TestNG.runSuites(TestNG.java:1144) at org.testng.TestNG.run(TestNG.java:1115) at com.revature.http.RunCucumber.doGet(RunCucumber.java:44) at javax.servlet.http.HttpServlet.service(HttpServlet.java:635) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:650) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Unknown Source)
请求协助排查并解决该空指针异常问题。
问题分析与解决方案
这个空指针异常发生在AbstractTestNGCucumberTests.features()方法的第29行,核心原因是Cucumber无法正确加载feature文件或初始化测试上下文,下面是几个针对性的解决步骤:
1. 检查CucumberRunner类的配置完整性
你的CucumberRunner类必须继承AbstractTestNGCucumberTests,并且通过@CucumberOptions明确指定feature文件路径和步骤定义包,示例如下:
package com.revature.cucumber; import cucumber.api.CucumberOptions; import cucumber.api.testng.AbstractTestNGCucumberTests; @CucumberOptions( features = "classpath:features", // 对应resources目录下的features文件夹 glue = "com.revature.stepdefinitions" // 步骤定义类所在的包 ) public class CucumberRunner extends AbstractTestNGCucumberTests { }
如果features路径错误或者feature文件缺失,Cucumber会因无法找到测试资源抛出NPE。
2. 确保feature文件在Tomcat类路径中
部署到Tomcat时,feature文件必须被打包到WEB-INF/classes目录下。如果是Maven项目,把feature文件放在src/main/resources/features目录下,Maven会自动将其打包到正确位置;如果是普通Web项目,手动将feature文件夹复制到WEB-INF/classes下。
3. 改用TestNG XML配置文件运行测试
在Servlet环境中直接调用testng.setTestClasses()可能会导致Cucumber上下文初始化不完整,改用TestNG XML配置文件可以更稳定地加载测试:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { TestListenerAdapter adapter = new TestListenerAdapter(); TestNG testng = new TestNG(); List<String> suiteFiles = Lists.newArrayList(); // 配置文件放在WEB-INF下,获取真实路径 suiteFiles.add(getServletContext().getRealPath("/WEB-INF/testng.xml")); testng.setTestSuites(suiteFiles); testng.addListener((ITestNGListener) adapter); testng.run(); }
对应的testng.xml内容:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="Cucumber Test Suite"> <test name="Cucumber Tests"> <classes> <class name="com.revature.cucumber.CucumberRunner"/> </classes> </test> </suite>
4. 验证依赖版本兼容性
Cucumber和TestNG的版本不兼容也可能导致初始化异常。比如Cucumber 4.x推荐搭配TestNG 6.14.x或7.0.x版本,你可以查看项目依赖,确保两者版本匹配。
5. 手动指定Cucumber资源路径
如果以上方法无效,可以在Servlet初始化时手动设置feature文件的系统属性,确保Cucumber能找到资源:
@Override public void init() throws ServletException { super.init(); // 设置feature文件的真实路径 String featurePath = getServletContext().getRealPath("/WEB-INF/classes/features"); System.setProperty("cucumber.features", featurePath); }
内容的提问来源于stack exchange,提问作者Antonio Tapia

