![](/img/trans.png)
[英]Integration test of Jersey web services using Spring for dependency injection fails to initialize @Context HttpServletRequest
[英]Servlet context injection fail while using jersey test framework
我從球衣開始,並試圖讓 freemarker 使用 TDD 來使用它。 我想為我的模板創建一個ViewProcessor
,但無法在類中注入 servlet 上下文。
這是課程代碼:
@Provider
public class myProcessor implements ViewProcessor<Template> {
[...]
@Context
public ServletContext myContext;
[...]
freemarkerConfiguration.setTemplateLoader(
new WebappTemplateLoader(myContext,
myContext.getInitParameter("freemarker.template.path")));
[...]
}
這是測試代碼:
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
public myProcessorTest() throws Exception{
super(new WebAppDescriptor.Builder("com.domain").build());
}
@Test
public void firstTest(){
mp = new myProcessor();
String path = new String("test.ftl");
Template template = mp.resolve(path);
assertNotNull(template);
}
}
我使用具有依賴項的 maven,如下所示:
<dependency>
<groupId>com.sun.jersey.jersey-test-framework</groupId>
<artifactId>jersey-test-framework-grizzly</artifactId>
<version>1.5-SNAPSHOT</version>
<scope>test</scope>
</dependency>
當我部署到本地碼頭服務器時,我的代碼運行良好。 但是如果我想在我的 IDE 中測試代碼,它無法注入 servlet 上下文( @Context
):當我運行測試時, myContext
為null
:/
我想我錯過了一些東西,但我是 servlet 世界的完整初學者。
有幾種方法可以做到。 刪除構造函數並實現一個 configure() 方法,如下所示:
public class myProcessorTest extends JerseyTest {
public static myProcessor mp;
@Override
protected AppDescriptor configure() {
return new WebAppDescriptor.Builder("com.domain")
.contextParam("contextConfigLocation", "classpath:/applicationContext.xml")
.contextPath("/").servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class)
.build();
}
或者,您可以使用 spring 上下文注釋您的測試:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class MyProcessorTest extends JerseyTest {
public static myProcessor mp;
假設您使用的是默認/標准的 Grizzy2 測試框架提供程序,則此問題有一個不需要 spring 的解決方案。 根據這個答案, jersey-test-framework-provider-grizzly2
框架提供者在構建應用程序上下文時不使用 servlet 環境。 您的症狀是由於沒有要注入的ServletContext
實例。
解決方法是自己為單元測試提供測試容器。 首先,修改您的依賴項:
<!--<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-grizzly2</artifactId>
<version>2.25</version>
<scope>test</scope>
</dependency>-->
<dependency>
<groupId>org.glassfish.jersey.test-framework</groupId>
<artifactId>jersey-test-framework-core</artifactId>
<version>2.25</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>2.25</version>
</dependency>
然后,修改您的測試以提供一個 Grizzy servlet 容器:
@Override
protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
return (final URI baseUri, final DeploymentContext deploymentContext) ->
new TestContainer() {
private HttpServer server = null;
@Override
public ClientConfig getClientConfig() {
return null;
}
@Override
public URI getBaseUri() {
return baseUri;
}
@Override
public void start() {
try {
this.server = GrizzlyWebContainerFactory.create(baseUri, Collections
.singletonMap("jersey.config.server.provider.packages", "<your-package-name>"));
} catch (final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
@Override
public void stop() {
this.server.shutdownNow();
}
};
}
我假設您將在多個單元測試中使用它,因此擴展JerseyTest
可能是明智的,以便可以自動執行此通用配置。 此外,可能值得查看org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory
以查看是否有您希望模擬/保留的測試容器提供的任何功能。 提供的示例應該能夠放入您的測試中,以至少確認這是一個修復程序。
編輯:在我自己的實現中,我需要在生成服務器時仍然提供ResourceConfig
的能力。 我懷疑這可能是其他 Jersey 測試框架用戶的常見情況。 建議的TestContainerFactory
工作示例如下。
import java.io.IOException;
import java.net.URI;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletContext;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.core.UriBuilder;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.grizzly.servlet.WebappContext;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.spi.TestContainer;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;
import org.glassfish.jersey.test.spi.TestHelper;
public class RestTestContainerFactory implements TestContainerFactory {
public static class RestTestContainer implements TestContainer {
private static final Logger LOGGER = Logger.getLogger(RestTestContainer.class.getName());
private URI baseUri = null;
private final HttpServer server;
public RestTestContainer(final URI baseUri, final DeploymentContext context) {
this.baseUri = UriBuilder.fromUri(baseUri).path(context.getContextPath()).build();
if(LOGGER.isLoggable(Level.INFO)) {
LOGGER.info("Creating RestRestContainer configured at the base URI "+TestHelper.zeroPortToAvailablePort(baseUri));
}
try {
final WebappContext webContext = new WebappContext("TestContext", context.getContextPath());
context.getResourceConfig()
.register(new AbstractBinder() {
@Override
protected void configure() {
bind(webContext).to(ServletContext.class);
}
});
this.server = GrizzlyHttpServerFactory.createHttpServer(this.baseUri, context.getResourceConfig(), false);
webContext.deploy(this.server);
} catch (final ProcessingException cause) {
throw new TestContainerException(cause);
}
}
@Override
public ClientConfig getClientConfig() {
return null;
}
@Override
public URI getBaseUri() {
return baseUri;
}
@Override
public void start() {
if(server.isStarted()) {
LOGGER.warning("Ignoring start request - RestTestContainer is already started");
} else {
LOGGER.fine("Starting RestTestContainer...");
try {
server.start();
if(baseUri.getPort() == 0) {
baseUri = UriBuilder.fromUri(baseUri)
.port(server.getListener("grizzly").getPort())
.build();
LOGGER.info("Started GrizzlyTestContainer at the base URI "+baseUri);
}
}
catch(final ProcessingException | IOException cause) {
throw new TestContainerException(cause);
}
}
}
@Override
public void stop() {
if(server.isStarted()) {
LOGGER.fine("Stopping RestTestContainer...");
server.shutdownNow();
} else {
LOGGER.warning("Ignoring stop request - RestTestContainer is already stopped");
}
}
}
@Override
public TestContainer create(final URI baseUri, final DeploymentContext context) {
return new RestTestContainer(baseUri,context);
}
}
GrizzlyWebContainerFactory
沮喪的是,grizzly 的GrizzlyWebContainerFactory
將提供一個 servlet 上下文,但沒有配置資源配置。 相反, GrizzlyHttpServerFactory
將使用ResourceConfig
配置應用程序,但不會提供 Web 上下文。
我們可以通過手動創建WebappContext
(擴展ServletContext
),配置它,然后通過AbstractBinder
將它注入資源配置來解決這個問題。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.