2017-02-13 133 views
0

我在运行针对Spring Boot REST控制器的JUnit测试时收到异常。我通过Postman测试了API,它按预期工作。不知道我在JUnit测试中缺少什么。Spring引导中REST API失败的JUnit测试

ProductController.java

@RestController 
@RequestMapping("/api") 
public class ProductController { 

    @Inject 
    private ProductRepository productRepository; 

    //URI: http://localhost:8080/api/products/50 
    @RequestMapping(value = "/products/{productId}", method = RequestMethod.GET) 
    public ResponseEntity<?> getProduct(@PathVariable Long productId) { 
     verifyProductExists(productId); 
     Product product = productRepository.findOne(productId); 
     return new ResponseEntity<>(product, HttpStatus.OK); 
    } 

    protected void verifyProductExists(Long productId) throws ResourceNotFoundException { 
     Product product = productRepository.findOne(productId); 
     if (product == null) { 
      throw new ResourceNotFoundException("Product with id " + productId + " not found..."); 
     } 
    } 

} 

ResourceNotFoundException.java

@ResponseStatus(HttpStatus.NOT_FOUND) 
public class ResourceNotFoundException extends RuntimeException { 

    private static final long serialVersionUID = 1L; 

    public ResourceNotFoundException() { 
    } 

    public ResourceNotFoundException(String message) { 
     super(message); 
    } 

    public ResourceNotFoundException(String message, Throwable cause) { 
     super(message, cause); 
    } 

} 

通过邮差:

http://localhost:8080/api/products/1 -> Returns 200 with Product data in JSON format 
http://localhost:8080/api/products/999 -> Returns 404 with Exception data in JSON format 

ProductRestClientTest.java

@RunWith(SpringJUnit4ClassRunner.class) 
public class ProductRestClientTest { 

    static final String VALID_PRODUCT_API_URI = "http://localhost:8080/api/products/35"; 
    static final String INVALID_PRODUCTS_API_URI = "http://localhost:8080/api/products/555"; 
    private RestTemplate restTemplate; 

    @Before 
    public void setUp() { 
     restTemplate = new RestTemplate(); 
    } 

    /* 
    Testing Happy Path scenario 
    */ 
    @Test 
    public void testProductFound() { 
     ResponseEntity<?> responseEntity = restTemplate.getForEntity(VALID_PRODUCT_API_URI, Product.class); 
     assert (responseEntity.getStatusCode() == HttpStatus.OK); 
    } 

    /* 
    Testing Error scenario 
    */ 
    @Test(expected = ResourceNotFoundException.class) 
    public void testProductNotFound() { 
     ResponseEntity<?> responseEntity = restTemplate.getForEntity(INVALID_PRODUCTS_API_URI, Product.class); 
     assert (responseEntity.getStatusCode() == HttpStatus.NOT_FOUND); 
    } 

    @After 
    public void tearDown() { 
     restTemplate = null; 
    } 

} 

异常而上述JUnit测试运行

Tests run: 2, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.759 sec <<< FAILURE! - in com.study.spring.boot.rest.ProductRestClientTest 
testProductNotFound(com.study.spring.boot.rest.ProductRestClientTest) Time elapsed: 0.46 sec <<< ERROR! 
java.lang.Exception: Unexpected exception, expected<com.study.spring.boot.rest.ResourceNotFoundException> but was<org.springframework.web.client.HttpClientErrorException> 
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) 
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700) 
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653) 
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613) 
    at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:312) 
    at com.study.spring.boot.rest.ProductRestClientTest.testProductNotFound(ProductRestClientTest.java:42) 
+0

你试图用@IntegrationTest来注释测试类? –

回答

1

该测试的问题在于用RestTemplate的404响应DefaultResponseErrorHandler方法handleError(ClientHttpResponse response)被触发。

在你的情况(返回你的404状态码 - >客户端错误),它会导致HttpClientErrorException

HttpStatus statusCode = getHttpStatusCode(response); 
    switch (statusCode.series()) { 
     case CLIENT_ERROR: 
      throw new HttpClientErrorException(statusCode, response.getStatusText(), 
        response.getHeaders(), getResponseBody(response), getCharset(response)); 

有对至少两种解决方案:

要么禁用默认的错误处理你的测试,可能提升你的setUp()方法,如:

restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){ 
     protected boolean hasError(HttpStatus statusCode) { 
      return false; 
     }}); 

,并清除(expected = ResourceNotFoundException.class)条款FR你的负面测试。因为在得到响应之后断言404并期望异常不会一起工作。

或使用MockMvc。它提供了更复杂的东西,并在默认情况下跳过DefaultResponseErrorHandler。

例如您的测试看起来是这样的:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 
import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; 

import org.junit.After; 
import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.junit4.SpringRunner; 
import org.springframework.test.web.servlet.MockMvc; 
import org.springframework.test.web.servlet.ResultActions; 
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; 
import org.springframework.web.context.WebApplicationContext; 

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 
public class ProductRestClientTestWithMockMvc { 

    private static final String PRODUCT_API_URI = "http://localhost:8080/api/products/{productId}"; 
    private MockMvc mockMvc = null; 

    @Autowired 
    private WebApplicationContext webApplicationContext; 

    @Before 
    public void before() throws Exception { 
     mockMvc = webAppContextSetup(webApplicationContext).build(); 
    } 

    @After 
    public void after() throws Exception { 
     mockMvc = null; 
    } 

    /* 
    * Testing Happy Path scenario 
    */ 
    @Test 
    public void testProductFound() throws Exception { 
     final MockHttpServletRequestBuilder builder = get(PRODUCT_API_URI, 35); 
     final ResultActions result = mockMvc.perform(builder); 
     result.andExpect(status().isOk()); 
    } 

    /* 
    * Testing Error scenario 
    */ 
    @Test 
    public void testProductNotFound() throws Exception { 
     final MockHttpServletRequestBuilder builder = get(PRODUCT_API_URI, 555); 
     final ResultActions result = mockMvc.perform(builder); 
     result.andExpect(status().isNotFound()); 
    } 

} 
0

的一点是,不必返回一个异常,但包含一个主体和一个HTTP消息HTTP状态码。在这种情况下,您会得到一个404代码,但没有人会将该代码翻译为异常。 为了获得想要的异常,需要指示restTemplate在遇到404时抛出ResourceNotFoundException。 基本上你需要一个错误处理程序:

RestTemplate restclient = new RestTemplate(); restclient.setErrorHandler(new MyResponseErrorHandler());

希望有帮助。

+0

MyResponseErrorHandler是我需要创建的新类吗? – user2325154

+0

是的。它必须实现http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/client/ResponseErrorHandler.html – Pirulino