【问题标题】:Retrieve a managed bean from a JerseyTest container with jersey-spring3使用 jersey-spring3 从 JerseyTest 容器中检索托管 bean
【发布时间】:2013-08-19 09:31:34
【问题描述】:

这个问题是上一个问题Specify Custom Application Context的后续问题。

我们正在将我们的一些数据服务从使用 jersey-spring 的 Jersey 1.x 迁移到使用 jersey-spring3 的 Jersey 2.x。

我们有几个继承自 JerseyTest 的测试类。其中一些类使用 web.xml 文件中未指定的自定义 applicationContext.xml 文件。

出于对象模拟的目的,我们将模拟我们 Jersey 资源中的一些组件。

在 Jersey 1.x 中,我们可以通过以下方式模拟应用程序上下文文件中的对象

<bean id="mockBean" class="org.easymock.EasyMock" 
    factory-method="createStrictMock" autowire="byName">
    <constructor-arg index="0" value="com.xxx.xxx.ClassToMock" /> 
</bean>

并按如下方式检索这些模拟实例

ClassToMock obj = (ClassToMock)ContextLoader
    .getCurrentWebApplicationContext()
    .getAutowireCapableBeanFactory()
    .getBean("mockBean");

Jersey 2.x 如何使用 jersey-spring3 实现同样的效果?

我已经梳理了API docsuser guides 和一些sources,但无法找到答案。

谢谢。

编辑:

我们将在 JAX-RS 资源中使用模拟 bean。我们的资源中有@Autowired 的服务接口。

例如

@Path(ProductResource.RESOURCE_PATH)
@Component
@Scope("prototype")
public class ProductResource 
extends GenericResource<Product, BaseModel> {

    /*
     * Members
     */

    public static final String RESOURCE_PATH = "product/";

    @Autowired
    protected ProductService productService;

    ...

我们想模拟这些服务并设定对这些服务的期望。

例如

<bean id="productService" class="org.easymock.EasyMock" 
    factory-method="createStrictMock">
    <constructor-arg index="0" 
        value="com.xxx.xxx.service.ProductService" /> 
</bean>

【问题讨论】:

  • 你能举例说明吗? (你如何/何时/在哪里使用模拟 bean?它是否在 JAX-RS 资源中?)你需要 WebApplicationContext 或任何 ApplicationContext 就足够了吗?
  • WebApplicationContextApplicationContext 都可以。这会给我们一个指向注入 JAX-RS 资源的 bean 的指针。

标签: java spring unit-testing jersey-2.0


【解决方案1】:

注意:我不是 Spring 专家,我认为这是一种解决方法,而不是推荐的方法。希望有人能提出更好的解决方案。

您无法通过调用 ContextLoader#getCurrentWebApplicationContext() 获得 ApplicationContext 实例,因为在使用 Jersey 测试框架 (JerseyTest) 进行单元/e2e 测试时,Jersey 2.x 运行时默认在 Servlet 容器之外初始化。

在这种情况下,您需要通过在测试包中实现ApplicationContextAware 接口来使用一些变通方法来获得ApplicationContext

public class ApplicationContextUtils implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) throws BeansException {
        ApplicationContextUtils.applicationContext = applicationContext;
    }
}

一旦你有了这个类,别忘了在你的应用上下文描述符中提到它:

...
<bean class="org.glassfish.jersey.examples.helloworld.spring.ApplicationContextUtils" />
...

您可以在测试中使用它:

public class JerseySpringResourceTest extends JerseyTest {

    // ... Configure ...

    @Before
    public void mockUp() throws Exception {
        // ApplicationContext is ready in your @Before methods ...
        assertThat(ApplicationContextUtils.getApplicationContext(), notNullValue());
    }

    @Test
    public void testJerseyResource() {
        // ... as well as in your test methods.
        assertThat(ApplicationContextUtils.getApplicationContext(), notNullValue());
    }
}

注意:如果您想将应用程序部署到 Servlet 容器并针对它运行 (JerseyTest) 测试,请参阅用户指南中的 Jersey Test Framework 章节(尤其是 External container 部分)。

【讨论】:

  • 感谢您的反馈。我会尽快给它一个测试。就一个问题。 this.applicationContext = applicationContext; 中的 setApplicationContext 不应该是 ApplicationContextUtils.applicationContext = applicationContext;,因为 applicationContext 是一个静态字段。
  • 还可以在 ApplicationContextUtils 上使用“@Component”注解(确保它位于组件扫描的包中),从而无需显式声明 bean。
【解决方案2】:

对于 Jersey 2.X 用户,这对我有用:

  public class AccountResourceTest extends JerseyTest {

    private ApplicationContext context;

    private BeanA beanA;

    private BeanB beanB;

    public AccountResourceTest() throws TestContainerException {
        super();

        beanA = context.getBean(BeanA.class);
        beanB = context.getBean(BeanB.class);
    }

    @Override
    protected Application configure() {
        context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        final ResourceConfig config = new JerseyConfiguration().property("contextConfig", context);
        return config;
    }

    @Override
    protected void configureClient(final ClientConfig config) {
        config.register(JacksonJsonProvider.class);
    }

    ...
}

这使我可以在我的 Jersey 测试中使用 JavaConfig,并且还可以访问上下文中的 bean。这是我得到这个想法的链接:http://geowarin.github.io/spring-boot/jersey/2014/01/31/a-simple-spring-boot-and-jersey-application.html

【讨论】:

    【解决方案3】:

    在 Jersey 版本 2.4.x 中,JerseyConfiguration 类不再存在,并被不理解 contextConfig 属性的ResourceConfig 取代。这是我的解决方案:

    package ch.vd.test;
    
    import java.net.URI;
    
    import javax.ws.rs.core.Application;
    
    import org.glassfish.hk2.api.ServiceLocator;
    import org.glassfish.jersey.server.ApplicationHandler;
    import org.glassfish.jersey.server.ResourceConfig;
    import org.glassfish.jersey.test.JerseyTest;
    import org.glassfish.jersey.test.grizzly.GrizzlyTestContainerFactory;
    import org.glassfish.jersey.test.spi.TestContainer;
    import org.glassfish.jersey.test.spi.TestContainerException;
    import org.glassfish.jersey.test.spi.TestContainerFactory;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    
    public class ExampleTest extends JerseyTest {
    
        private ServiceLocator serviceLocator;
    
        @Override
        public void setUp() throws Exception {
            super.setUp();
            final ApplicationContext context = serviceLocator.getService(ApplicationContext.class, "SpringContext");
            final Object bean = context.getBean("someBean");
        }
    
        @Override
        protected Application configure() {
            final ResourceConfig config = new ResourceConfig(RestResources.class);
            config.property("contextConfigLocation", "classpath:example-context.xml");
            return config;
        }
    
        @Override
        protected TestContainerFactory getTestContainerFactory() throws TestContainerException {
            return new GrizzlyTestContainerFactory() {
                @Override
                public TestContainer create(URI uri, ApplicationHandler appHandler) throws IllegalArgumentException {
                    serviceLocator = appHandler.getServiceLocator();
                    return super.create(uri, appHandler);
                }
            };
        }
    
        @Test
        public void testStuff() throws Exception {
            ...
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果您对此没有任何异议,您可以将测试类注入 Jersey 上下文。

      例如:

      @Override
      protected Application configure() {
          final TestJerseyApplication application = new TestJerseyApplication();
      
          final Map<String, Object> properties = new HashMap<>();
          properties.put("contextConfigLocation", "classpath:test-spring-context.xml");
          application.setProperties(properties);
      
          application.register(this);
          return application;
      }
      

      之后,@Autowired 注释将为您工作。

      【讨论】:

      • 啊,我们可以告诉 Jersey 通过“contextConfigLocation”而不是默认的“applicationContext.xml”名称来查找要查找的自定义 Spring 上下文文件。
      猜你喜欢
      • 2014-06-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-18
      • 2014-02-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多