Java 强制Jersey读取Jersey测试中的模拟

Java 强制Jersey读取Jersey测试中的模拟,java,spring,unit-testing,junit,jersey,Java,Spring,Unit Testing,Junit,Jersey,我想用球衣测试一个资源。我创建了以下测试: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = "classpath:testApplicationContext.xml") public class ResourceTest extends JerseyTest { @Configuration public static class Config { @Be

我想用球衣测试一个资源。我创建了以下测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:testApplicationContext.xml")
public class ResourceTest extends JerseyTest
{
    @Configuration
    public static class Config
    {
        @Bean
        public AObject aObject()
        {
            return mock(AObject.class);
        }
    }

    @Autowired
    public AObject _aObject;

    @Test
    public void testResource()
    {
        // configouring mock _aObject

        Response response = target("path");
        Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
    }


    @Override
    protected Application configure()
    {
        return new ResourceConfig(Resource.class).property("contextConfigLocation", "classpath:testApplicationContext.xml");
    }
}
我的资源还有一个带有
@Autowired
注释的AOObject引用

我的问题是我的
JerseyTest
资源
(由测试配置)对于模拟对象有不同的实例。在控制台中,我看到
testApplicationContext.xml
加载了两次,一次用于测试,一次用于资源


如何强制jersey使用相同的模拟?

调试jersey-spring3(版本2.9.1)库后,问题似乎出在SpringComponentProvider.createSpringContext中

private ApplicationContext createSpringContext() {
    ApplicationHandler applicationHandler = locator.getService(ApplicationHandler.class);
    ApplicationContext springContext = (ApplicationContext) applicationHandler.getConfiguration().getProperty(PARAM_SPRING_CONTEXT);
    if (springContext == null) {
        String contextConfigLocation = (String) applicationHandler.getConfiguration().getProperty(PARAM_CONTEXT_CONFIG_LOCATION);
        springContext = createXmlSpringConfiguration(contextConfigLocation);
    }
    return springContext;
}
它检查应用程序属性中是否存在名为“contextConfig”的属性,如果不存在,则初始化spring应用程序上下文。 即使您在测试中初始化了spring应用程序上下文,jersey也会创建另一个上下文并使用该上下文。因此,我们必须通过Jersey应用程序类测试中的ApplicationContext。解决方案如下:

@ContextConfiguration(locations = "classpath:jersey-spring-applicationContext.xml")
public abstract class JerseySpringTest
{
    private JerseyTest _jerseyTest;

    public final WebTarget target(final String path)
    {
        return _jerseyTest.target(path);
    }

    @Before
    public void setup() throws Exception
    {
        _jerseyTest.setUp();
    }

    @After
    public void tearDown() throws Exception
    {
        _jerseyTest.tearDown();
    }

    @Autowired
    public void setApplicationContext(final ApplicationContext context)
    {
        _jerseyTest = new JerseyTest()
        {
            @Override
            protected Application configure()
            {
                ResourceConfig application = JerseySpringTest.this.configure();
                application.property("contextConfig", context);

                return application;
            }
        };
    }

    protected abstract ResourceConfig configure();
}
上面的类将从我们的测试中获取应用程序上下文,并将其传递给已配置的ResourceConfig,以便SpringComponentProvider将相同的应用程序上下文返回给jersey。我们还使用jersey-spring-applicationContext.xml来包含特定于jersey的spring配置

我们无法从JerseyTest继承,因为它在初始化测试应用程序上下文之前在构造函数中初始化应用程序

例如,现在可以使用这个基类创建测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:testContext.xml")
public class SomeTest extends JerseySpringTest
{
     @Autowired
     private AObject _aObject;

     @Test
     public void test()
     {
          // configure mock _aObject when(_aObject.method()).thenReturn() etc...

         Response response = target("api/method").request(MediaType.APPLICATION_JSON).get();
         Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
     }

     @Override
     protected ResourceConfig configure()
     {
        return new ResourceConfig(MyResource.class);
     }
}
在testContext.xml中,添加以下定义以注入模拟AOObject

<bean class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.yourcompany.AObject" />
</bean>

我无法从@Grigoris working那里得到答案,尽管他对为什么会发生这种情况的解释是正确的

最后,我选择了下面的方法,它公开了一个特殊的setter来插入模拟对象。不像上面的方法那样“干净”,但值得权衡的是公开我想要模拟的apiProvider,这样我就可以编写一些测试了

public MyAPITest extends JerseyTest {

    // Declare instance of the API I want to test - this will be instantiated in configure()
    MyAPI myAPI;

    @Override
    protected ResourceConfig configure()
    {  
        MockitoAnnotations.initMocks(this);
        myAPI = new MyAPI();

        ResourceConfig resourceConfig = new ResourceConfig();
        resourceConfig.register(MyAPI).property("contextConfig", new ClassPathXmlApplicationContext("classpath:spring.testHarnessContext.xml"));
        return resourceConfig;
    }

    @Mock
    private MyAPIProvider mockAPIProvider;

    @Before
    public void before() {
        myAPI.setMockProvider(mockAPIProvider);
    }


    @Test
    public void test() {

        // I can now define the mock behaviours and call the API and validate the outcomes
        when(mockAPIProvider....)
        target().path("....)            
    }
}

如果有人对Kevin针对Jersey v1的解决方案感兴趣:

public MyAPITest extends JerseyTest {

    @InjectMocks
    MyAPI myAPI;

    @Mock
    MyApiService myApiService;

    @Override
    protected AppDescriptorconfigure()
    {  
        MockitoAnnotations.initMocks(this);

        ResourceConfig rc = new DefaultResourceConfig();
        rc.getSingletons().add(myAPI);

        return new LowLevelAppDescriptor.Builder(rc).contextPath("context").build();
    }

    @Test
    public void test() {
        // I can now define the mock behaviours
        when(myApiService...)

        WebResource webResource = resource().path("mypath");
        ClientResponse result = webResource.get(ClientResponse.class);            
    }
}

非常感谢您的回答,它解决了我使用jersey测试框架和spring测试开发集成测试的问题。特别是,我的问题是在同一ApplicationContext中的测试执行期间指示模拟对象。谢谢大家,