2014-07-01 191 views
8

閱讀嘲笑我想測試與JerseyTest一個地質礦產。我創建了以下測試:隊球衣從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參考。

我的問題是我的JerseyTestResource(由測試配置)對於Mock對象有不同的實例。在控制檯中,我看到testApplicationContext.xml加載了兩次,一次用於測試,一次用於資源。

我怎麼能強迫球衣使用相同的模擬?

回答

15

調試球衣,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 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-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

偉大的答案!謝謝 –

+0

非常感謝這個答案,它解決了我用球衣測試框架和彈簧測試開發集成測試的問題。特別是我的問題是在同一個ApplicationContext中的測試執行期間指示模擬對象。感謝所有人, – bifulcoluigi

1

我無法從@Grigoris工作答案https://stackoverflow.com/a/24512682/156477,雖然他的原因是什麼,解釋是正確的。

最後我去了下面這暴露了一個特殊的二傳手插入模擬對象的方法。還不如「乾淨」的上述做法,但值得暴露我想嘲笑所以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("....)    
    } 
} 
0

如果有人有興趣的解決方案https://stackoverflow.com/a/40591082/4894900從凱文·澤西島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);    
    } 
}