我想用JerseyTest测试资源。我创建了以下测试:
@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
注释的AObject引用。
我的问题是我的JerseyTest
和Resource
(由测试配置)具有Mock对象的不同实例。在控制台中,我看到testApplicationContext.xml
被加载两次,一次用于测试,一次用于资源。
我如何强迫球衣使用相同的模拟?
答案 0 :(得分:16)
调试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 Application类中的测试中传递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来包含特定于针织物的弹簧配置。
我们不能从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中添加以下定义以注入模拟AObject。
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.yourcompany.AObject" />
</bean>
答案 1 :(得分:1)
我无法从@Grigoris那里得到答案https://stackoverflow.com/a/24512682/156477,尽管他对其发生原因的解释是正确的。
最后,我采用下面的方法,公开了一个特殊的setter来插入模拟对象。不像&#39;清洁&#39;作为上面的方法,但值得权衡暴露我想要模拟的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("....)
}
}
答案 2 :(得分:0)
如果有人对Kevin for Jersey v1的解决方案https://stackoverflow.com/a/40591082/4894900感兴趣:
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);
}
}