【问题标题】:Servlet context injection fail while using jersey test framework使用 jersey 测试框架时 Servlet 上下文注入失败
【发布时间】:2011-05-19 02:30:45
【问题描述】:

我从 jersey 开始,并试图让 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):当我运行测试时,myContextnull:/

我想我遗漏了一些东西,但我是 servlet 世界的初学者。

【问题讨论】:

    标签: java testing jersey freemarker servlets


    【解决方案1】:

    这是一种使用 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++;
        }
    }
    

    【讨论】:

      【解决方案2】:

      假设您使用的是默认/标准的 Grizzy2 测试框架提供程序,则有一个不需要 spring 的解决方案。根据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 可能是明智之举,以便可以自动执行此通用配置。此外,可能值得查看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 将其注入资源配置来解决此问题。

      【讨论】:

        【解决方案3】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-13
          • 2012-12-29
          • 2014-08-31
          • 1970-01-01
          • 1970-01-01
          • 2019-07-07
          相关资源
          最近更新 更多