假设您使用的是默认/标准的 Grizzy2 测试框架提供程序,则有一个不需要 spring 的解决方案。根据this answer,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 Test Framework 用户的常见情况。下面是提议的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);
}
}
令人沮丧的是,grizzly 的 GrizzlyWebContainerFactory 将提供一个 servlet 上下文,但不会使用资源配置进行配置。相反,GrizzlyHttpServerFactory 将使用ResourceConfig 配置应用程序,但不会提供 Web 上下文。
我们可以通过手动创建WebappContext(扩展ServletContext),对其进行配置,然后通过AbstractBinder 将其注入资源配置来解决此问题。