18

我正在对 REST 控制器 POST 处理程序进行集成测试。嗯,我正在努力。

它给了我 HttpMessageNotReadableException 异常:无法读取 JSON:由于输入结束,没有要映射的内容

这是我的控制器:

@Controller
@RequestMapping("admin")
public class AdminController {

    private static Logger logger = LoggerFactory.getLogger(AdminController.class);

    private static final String TEMPLATE = "Hello, %s!";

    @Autowired 
    private AdminService adminService;

    @Autowired
    private AdminRepository adminRepository;

    @RequestMapping(value = "crud", method = RequestMethod.POST, produces = "application/json; charset=utf-8")
    @ResponseBody
    public ResponseEntity<Admin> add(@RequestBody Admin admin, UriComponentsBuilder builder) {
        AdminCreatedEvent adminCreatedEvent = adminService.add(new CreateAdminEvent(admin.toEventAdmin()));
        Admin createdAdmin = Admin.fromEventAdmin(adminCreatedEvent.getEventAdmin());
        HttpHeaders responseHeaders = new HttpHeaders();
        responseHeaders.add("Content-Type", "application/json; charset=utf-8");
        responseHeaders.setLocation(builder.path("/admin/{id}").buildAndExpand(adminCreatedEvent.getAdminId()).toUri());
        return new ResponseEntity<Admin>(createdAdmin, responseHeaders, HttpStatus.CREATED);
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public String handleException(HttpMessageNotReadableException e) {
        return e.getMessage();
    }

}

基础测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration( classes = { ApplicationConfiguration.class, WebSecurityConfig.class, WebConfiguration.class, WebTestConfiguration.class })
@Transactional
public abstract class AbstractControllerTest {

    @Autowired
    private WebApplicationContext webApplicationContext;

    @Autowired
    private FilterChainProxy springSecurityFilterChain;

    protected MockHttpSession session;

    protected MockHttpServletRequest request;

    protected MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.webApplicationContext).addFilters(this.springSecurityFilterChain).build();
    }

}

集成测试:

@Test
public void testAdd() throws Exception {
    HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + "mypassword");
    this.mockMvc.perform(
        post("/admin/crud").headers(httpHeaders)
        .param("firstname", "Stephane")
        .param("lastname", "Eybert")
        .param("login", "stephane")
        .param("password", "toto")
    ).andDo(print())
    .andExpect(
        status().isOk()
    ).andReturn();
}

控制台日志必须说什么:

2013-11-04 19:31:23,168 DEBUG  [HttpSessionSecurityContextRepository] SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@158ddda0: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@158ddda0: Principal: org.springframework.security.core.userdetails.User@552e813c: Username: stephane; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_ADMIN; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ADMIN'
2013-11-04 19:31:23,168 DEBUG  [RequestResponseBodyMethodProcessor] Written [Could not read JSON: No content to map due to end-of-input
 at [Source: UNKNOWN; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
 at [Source: UNKNOWN; line: 1, column: 1]] as "application/json;charset=utf-8" using [org.springframework.http.converter.StringHttpMessageConverter@10d328]
2013-11-04 19:31:23,169 DEBUG  [TestDispatcherServlet] Null ModelAndView returned to DispatcherServlet with name '': assuming HandlerAdapter completed request handling
2013-11-04 19:31:23,169 DEBUG  [TestDispatcherServlet] Successfully completed request
2013-11-04 19:31:23,169 DEBUG  [ExceptionTranslationFilter] Chain processed normally
2013-11-04 19:31:23,169 DEBUG  [SecurityContextPersistenceFilter] SecurityContextHolder now cleared, as request processing completed
MockHttpServletRequest:
         HTTP Method = POST
         Request URI = /admin/crud
          Parameters = {firstname=[Stephane], lastname=[Eybert], login=[stephane], password=[toto]}
             Headers = {Content-Type=[application/json], Accept=[application/json], Authorization=[Basic c3RlcGhhbmU6bXlwYXNzd29yZA==]}

             Handler:
                Type = com.thalasoft.learnintouch.rest.controller.AdminController
              Method = public org.springframework.http.ResponseEntity<com.thalasoft.learnintouch.rest.domain.Admin> com.thalasoft.learnintouch.rest.controller.AdminController.add(com.thalasoft.learnintouch.rest.domain.Admin,org.springframework.web.util.UriComponentsBuilder)

               Async:
   Was async started = false
        Async result = null

  Resolved Exception:
                Type = org.springframework.http.converter.HttpMessageNotReadableException

        ModelAndView:
           View name = null
                View = null
               Model = null

            FlashMap:
MockHttpServletResponse:
              Status = 200
       Error message = null
             Headers = {Content-Type=[application/json;charset=utf-8], Content-Length=[254]}
        Content type = application/json;charset=utf-8
                Body = Could not read JSON: No content to map due to end-of-input
 at [Source: UNKNOWN; line: 1, column: 1]; nested exception is com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
 at [Source: UNKNOWN; line: 1, column: 1]
       Forwarded URL = null
      Redirected URL = null
             Cookies = []
2013-11-04 19:31:23,177 DEBUG  [TransactionalTestExecutionListener] No method-level @Rollback override: using default rollback [true] for test context [TestContext@ce4625 testClass = AdminControllerTest, testInstance = com.thalasoft.learnintouch.rest.AdminControllerTest@1b62fcd, testMethod = testAdd@AdminControllerTest, testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@9be79a testClass = AdminControllerTest, locations = '{}', classes = '{class com.thalasoft.learnintouch.rest.config.ApplicationConfiguration, class com.thalasoft.learnintouch.rest.config.WebSecurityConfig, class com.thalasoft.learnintouch.rest.config.WebConfiguration, class com.thalasoft.learnintouch.rest.config.WebTestConfiguration}', contextInitializerClasses = '[]', activeProfiles = '{}', resourceBasePath = 'src/main/webapp', contextLoader = 'org.springframework.test.context.web.WebDelegatingSmartContextLoader', parent = [null]]]

有什么线索吗?

4

2 回答 2

11

在我看来,问题在于内容格式。您的端点期望数据将作为application/json发送,但在测试中您将其作为application/x-www-form-urlencoded发送(在请求中设置正确的内容类型标头并不重要)。尝试以 json 格式发送管理对象(作为请求的主体):

{
 "firstname" : "Stephane",
 "lastname" : "Eybert",
 "login" : "stephane",
 "password" : "toto"
}

顺便说一句,/admin/crudREST 资源寻址规则不适用,您应该将其更改为/admin. crud (CREATE, READ, UPDATE, DELETE) 将映射到 HTTP 方法 (POST, GET, PUT, DELETE)

于 2013-11-05T06:14:26.980 回答
2

我用 .content() 替换了 .param() 方法:

        post("/admin/crud").headers(httpHeaders)
        .contentType(MediaType.APPLICATION_JSON)
        .accept(MediaType.APPLICATION_JSON)
        .content("{ \"firstname\" : \"" + admin0.getFirstname() + "\", \"lastname\" : \"" + admin0.getLastname() + "\", \"email\" : \"" + admin0.getEmail() + "\", \"login\" : \"" + admin0.getLogin() + "\", \"password\" : \"" + admin0.getPassword() + "\", \"passwordSalt\" : \"" + admin0.getPasswordSalt() + "\" }")
    ).andDo(print())
    .andExpect(status().isCreated())
    .andExpect(jsonPath("$.firstname").value(admin0.getFirstname()))
    .andExpect(jsonPath("$.lastname").value(admin0.getLastname()))
    .andExpect(jsonPath("$.email").value(admin0.getEmail()))
    .andExpect(jsonPath("$.login").value(admin0.getLogin()))
    .andExpect(jsonPath("$.password").value(admin0.getPassword()))
    .andExpect(jsonPath("$.passwordSalt").value(admin0.getPasswordSalt()))
    .andReturn();

现在它按预期工作。

于 2013-11-05T14:59:21.220 回答