我正在尝试为弹簧 mvc rest 控制器和控制器访问的服务创建一个 JUnit 测试用例。
我正在使用Mockito来做上述事情。我能够成功地从测试用例中调用模拟注入的控制器,并且在调试时,我看到模拟的用户服务在模拟的控制器中可用,但服务对象中的方法没有被调用。调试时,我观察到它只是跳过服务方法调用。
我也没有看到任何例外。
我正在使用
- 马文 3
- 春季 4.1.1 发布版本
- 朱尼特 4.11
- Java 版本 1.6
我在下面粘贴了我的代码:
1. 测试类
package controller;
import java.io.IOException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.RequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/spring/application-config.xml")
public class UserControllerTest {
private MockMvc mockMvc;
@Mock
private IUserService userService;
@InjectMocks
private UserController controller;
@Before
public void setup(){
MockitoAnnotations.initMocks(this);
this.mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
@Test
public void testRegisterUser()throws Exception{
User user = new User();
user.setCity("City");
user.setTown("Town");
user.setFirstname("Name");
user.setLastname("LastName");
user.setPassword("abc@123");
user.setUsername("abc@gmail.com");
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
}
public static byte[] convertObjectToJsonBytes(Object object) throws IOException {
ObjectMapper mapper = new ObjectMapper();
return mapper.writeValueAsBytes(object);
}
}
2. 被测控制器
package com.myPackage.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.myPackage.model.User;
import com.myPackage.service.IUserService;
@RestController
@RequestMapping("/service/user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private IUserService userService;
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
@RequestMapping(value = "/register/", method = RequestMethod.POST,headers="Accept=application/json")
public String registerUser(@RequestBody User user) {
logger.info("register user:"+user.toString());
String userDetails = userService.registerUser(user);
logger.info("user registered:"+userDetails);
return userDetails;
}
}
3. 测试中要在控制器内调用的服务
服务方法注册用户将从上面的控制器调用。当我们运行测试用例时,我没有看到控制台上打印任何日志。同样在调试时,我看到像这样的用户服务实例 - IUserService$$EnhancerByMockitoWithCGLIB$$c 00081eb 被创建,但是当我深入挖掘所有内容以查看模拟处理程序注册方法下的方法列表时,我只在调用列表中看到"toString"方法。不确定这是否说明了为什么下面的服务类中的此方法"registerUser"在测试用例期间没有被调用。
package com.myPackage.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import com.myPackage.dao.IUserDao;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
@Service("userService")
public class UserService implements IUserService {
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
@Autowired
private IUserDao userDao;
public IUserDao getUserDao() {
return userDao;
}
public void setUserDao(IUserDao userDao) {
this.userDao = userDao;
}
@Override
public String registerUser(User user) {
// TODO Auto-generated method stub
logger.info("New User for registration:",user);
if(user!=null && user.getUsername()!=null){
User alreadyExist = userDao.getUserByLogin(user.getUsername());
if(alreadyExist!=null){
return "userAlreadyExists";
}
}
logger.info("New User for registration complete:",user.getUser_id());
return null;
}
}
4. 由上述第 3 点中的用户服务类实现的接口
IUserService.上面测试类中模拟的用户服务类型为 IUserService。
package com.myPackage.service;
import com.myPackage.model.User;
import com.myPackage.model.UserInfo;
public interface IUserService {
public String registerUser(User user);
}
userService 中的方法(读取代码(实际上永远不会从控制器调用,因为它已被 mockito 模拟出来。
您已经通过执行以下操作自己定义了这一点:
@Mock
private IUserService userService;
@InjectMocks
private UserController controller;
MockitoAnnotations.initMocks(this);
如果你想断言该方法已被调用,你可以使用,
verify(userService, times(1)).registerUser(any(User.class));
@Jonas:感谢您的见解。如果我不模拟用户服务,那么在单元测试时,用户服务在用户控制器方法匹配注册下发现为空。所以在某处读完后,我嘲笑了用户服务。同样在按照您的建议进行更改后,模拟服务方法仍然没有被调用 - 以下是我的更改 -
RequestBuilder builder = MockMvcRequestBuilders.post("/service/user/register/")
.contentType(MediaType.APPLICATION_JSON).content(convertObjectToJsonBytes(user));
mockMvc.perform(builder).andExpect(MockMvcResultMatchers.status().isOk());
Mockito.verify(userService, Mockito.times(1)).registerUser(Mockito.any(User.class));
如果要测试实际的服务方法,请自动连线:
@Autowired
private IUserService userService;
如果你想像现在这样使用模拟,你需要存根该方法:
when(userService.registerUser(any(User.class))).thenReturn("expected string");