我正在尝试对我的代码进行一些单元/集成测试,
我有一个典型的使用spring boot的rest应用程序构建,我正在尝试测试AdminController save
方法:
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<Void> save(@RequestBody @Valid User user) {
user.setRole(Role.ROLE_ADMIN);
user.setPassword(passwordEncoder.encode(user.getPassword()));
return super.save(user);
}
该方法很简单,它通过数据库检查副本,因为我使User对象的username
属性唯一:
@NotNull
@Column(updatable = false, unique = true)
private String username;
当我尝试使用相同的用户名保存两个用户时,服务器拒绝使用此out-of-the-box
JSON输出:( Http代码:500)
{
"timestamp": 1473942296273,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.dao.DataIntegrityViolationException",
"message": "could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
"path": "/api/admins"
}
我对此感到满意,因为看起来Spring启动默认行为是将错误发送到/error
(由BasicErrorController
处理)来处理异常并返回这个漂亮的json输出:
[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement] with root cause
DispatcherServlet : DispatcherServlet with name 'dispatcherServlet' processing POST request for [/error]
RequestMappingHandlerMapping : Looking up handler method for path /error
RequestMappingHandlerMapping : Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)]
DefaultListableBeanFactory : Returning cached instance of singleton bean 'basicErrorController'
OpenEntityManagerInViewInterceptor : Opening JPA EntityManager in OpenEntityManagerInViewInterceptor
HttpEntityMethodProcessor : Written [{timestamp=Thu Sep 15 16:09:07 AST 2016, status=500, error=Internal Server Error, exception=org.springframework.dao.DataIntegrityViolationException, message=could not execute statement; SQL [n/a]; constraint [uk_sb8bbouer5wak8vyiiy4pf2bx]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement, path=/api/admins}] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@1482cc8]
但是当我尝试使用mockito(see full test class)测试时:
@Transactional
@Test
public void testSaveDupValidUser() throws Exception {
User user = new User();
user.setUsername("admin");
this.mockMvc.perform(post("/api/admins")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andDo(print())
.andExpect(status().isOk());
this.mockMvc.perform(post("/api/admins")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(user)))
.andDo(print())
.andExpect(status().isInternalServerError());
}
而不是获得500
内部服务器错误,我从junit获得了以下异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["UK_SB8BBOUER5WAK8VYIIY4PF2BX_INDEX_3 ON PUBLIC.""user""(USERNAME) VALUES ('admin', 1)"; SQL statement:
insert into "user" (id, created_by, created_date, modified_by, modified_date, enabled, password, role, username) values (null, ?, ?, ?, ?, ?, ?, ?, ?) [23505-192]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
所以,我的问题是:
如何启用调度程序servlet转发到/ error并返回相同的错误,就像应用程序正在运行(未测试)一样?
同时保持使用MockMvc standealone,而不在rest方法中编写代码来处理异常并返回代码500
。
(我认为这可能是不可能的,所以我应该在异常上断言吗?或者我应该使用webAppContextSetup
?这里最好的做法是什么? - 例如jHipster
使用独立的MockMvc它的测试)
相关:unit test Spring MissingServletRequestParameterException JSON response(但我的未修复)
答案 0 :(得分:1)
在运行时,spring在servlet容器中注册一个错误页面,并在那里转发所有未处理的错误。
MockMvcServer并不完全支持转发,我认为这就是您在测试中看到不同结果的原因。
保存用户失败时,因为已经存在另一个具有相同名称的用户,这不是(意外的)内部服务器错误。相反,您应该在控制器中捕获异常并返回HTTP 400(错误请求)。这告诉客户端,服务器没问题,但是请求的内容不是。 您可以添加ExceptionHandler来为不同类型的例外创建响应。
所有HTTP 400-499都是客户端错误,因此不会将它们转发到错误页面。
如果处理了异常,您还应该在MockMvc测试中收到正确的Http状态和响应。