使用Jersey测试框架时,Servlet上下文注入失败。

10

我刚开始使用Jersey,并尝试使用TDD使Freemarker与其配合工作。我想为我的模板制作一个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>

我的代码能够在本地jetty服务器上成功运行。但是当我想要在我的IDE中测试代码时,它无法注入servlet上下文 (@Context):当我运行测试时,myContextnull :/

我认为我缺少某些东西,但我是一个完全的servlet初学者。

3个回答

2
这是一种使用Jersey测试框架和Servlet支持来测试特定资源类的技术。还演示了如何自定义ServletContext
import javax.servlet.ServletContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Feature;
import javax.ws.rs.core.FeatureContext;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.servlet.ServletContainer;
import org.glassfish.jersey.test.DeploymentContext;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.ServletDeploymentContext;
import org.glassfish.jersey.test.TestProperties;
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.glassfish.jersey.test.spi.TestContainerException;
import org.glassfish.jersey.test.spi.TestContainerFactory;

import static org.mockito.Mockito.mock;

/**
 * A base class for testing web resources.
 */
public abstract class WebResourceTest extends JerseyTest {

    /**
     * Creates a JAX-RS resource configuration for test purposes.
     */
    @Override
    protected abstract ResourceConfig configure();

    /**
     * Creates a test container factory with servlet support.
     */
    @Override
    protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
        return new GrizzlyWebTestContainerFactory();
    }

    /**
     * Configures a deployment context for JAX-RS.
     */
    @Override
    protected DeploymentContext configureDeployment() {
        ResourceConfig app = configure();
        app.register(new Feature() {
            @Context
            ServletContext servletContext;

            @Override
            public boolean configure(FeatureContext context) {
                servletContext.setAttribute("example", new Object());
                return true;
            }
        });
        return ServletDeploymentContext.forServlet(new ServletContainer(app)).build();
    }
}

一个使用示例:

import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.core.Context;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.core.Response;
import static org.mockito.Mockito.spy;
import static org.testng.Assert.assertEquals;
import static org.junit.Assert.*;

public class MyResourceTest extends WebResourceTest {

    private MyResource resource;

    @Override
    protected ResourceConfig configure() {
        resource = spy(new MyResource());
        return new ResourceConfig().register(resource);
    }

    @Test
    public void testSomething() {
        Response r = target("/myresource").request().get();
        assertEquals(200, r.getStatus());
        assertEquals(1, resource.count);
    }
}

@Path("/myresource")
public class MyResource {
    int count = 0;

    @Context
    protected ServletContext servletContext;

    @GET
    public void get() {
       Object attr = servletContext.getAttribute("example");
       count++;
    }
}

0

有几种方法可以做到这一点。删除构造函数并实现一个像这样的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;

0

有一个解决这个问题的方法,不需要使用Spring,假设您正在使用默认/标准的Grizzy2测试框架提供程序。根据this answerjersey-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,以便可以自动执行此公共配置。此外,可能值得回顾{{link1:org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory}},以查看测试容器提供的任何功能是否需要模拟/保留。提供的示例应该可以直接用于你的测试程序,至少可以确认这是一个修复方法。

编辑:在我的实现中,我需要在生成服务器时仍然能够提供ResourceConfig。我认为其他Jersey Test框架用户可能也有类似需求。下面是建议的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 将其注入到资源配置中来解决这个问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接