如何将数据模型模拟注入 spring 控制器?
How to inject data-model mock into spring controller?
在我的单元测试中,我需要在 spring @Controller 中模拟一个数据模型实例,否则 @RequestMapping 方法的 return 值会出错。
为此,我尝试了以下操作:
- 创建一个用户模拟,它调用 user.login() 并且需要 return "true"
- 在 LoginController 中注入模拟对象
- 将登录方法存根为 return true
- 执行POST/使用来自spring测试的MockMVC登录
- 验证 mockUser.login 是否被调用
控制器方法如下:
@RequestMapping(value = "/Login", method = RequestMethod.POST)
public String updateUI(Locale locale, Model model, @RequestParam("username") String username,
@RequestParam("hashedPW") String hashedPW, HttpServletRequest request) {
model.addAttribute("username", username);
user = new User(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
boolean loginResult = user.login();
if(loginResult == true) {
return "profile";
}
String output = "Failed login (" + username + ") requested, locale = " + locale;
log(output);
return "home";
}
以及我使用注入初始化模拟对象:
@Mock
private User mockUser;
@InjectMocks
private LoginController injectedLoginController;
@Before
public void setup() throws ServletException {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(injectedLoginController).build();
LOGFILE = new File("logs/general.log");
}
最后是单元测试:
@Test
public void testLoginSuccess() throws Exception {
String username = "Stefan";
byte[] hashedPW = "".getBytes();
when(mockUser.login()).thenReturn(true);
ResultActions ra = mvc
.perform(post("/Login").param("username", username).param("hashedPW", hashedPW.toString()))
.andExpect(status().isOk());
verify(mockUser).login();
}
总而言之,我希望控制器处理的 User 对象是 mockUser 类型而不是 User,并且 login() 方法被调用一次(相应地)并且 return "true".
但我得到的只是
“需要但未调用:mockUser.login()
实际上,与此模拟的交互为零。"
我很感激任何解决我问题的建议,因为我已经为此工作了很长一段时间,但我似乎没有找到窍门。
1) 这似乎是一个 IT 测试.. 你不应该强制任何注入.. spring 这样做:
@InjectMocks
private LoginController injectedLoginController;
2) 你需要监视你的控制器:
@SpyBean
private LoginController injectedLoginController;
3) 您需要将 User
的创建移动到控制器内的包级别方法:
user = createUser(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
...
User createUser(...){
return new User(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
}
4) 使该方法 return 成为模拟用户:
doReturn(mockUser).when(injectedLoginController).createUser(...);
when(mockUser.login()).thenReturn(true);
如果您不想更改代码,可以从 PowerMockito
获得帮助,这有助于模拟构建新对象。
@RunWith(PowerMockRunner.class)
@PrepareForTest({User.class})
public class ControllerUnderTest {
// here User is class for which we want to mock object creation
}
现在,让我们模拟对象
@Test
public void testLoginSuccess() throws Exception {
....
User userMock = PowerMockito.createMock(User.class);
PowerMockito.whenNew(User.class).withArguments(username, username, hashedPW.getBytes(), LoginHandler.getInstance()).thenReturn(userMock);
expect(userMock.login()).andReturn(true);
verify(mockUser).login();
...
}
有关 powermock
的更多详细信息,请查看此 PowerMockito
在我的单元测试中,我需要在 spring @Controller 中模拟一个数据模型实例,否则 @RequestMapping 方法的 return 值会出错。
为此,我尝试了以下操作:
- 创建一个用户模拟,它调用 user.login() 并且需要 return "true"
- 在 LoginController 中注入模拟对象
- 将登录方法存根为 return true
- 执行POST/使用来自spring测试的MockMVC登录
- 验证 mockUser.login 是否被调用
控制器方法如下:
@RequestMapping(value = "/Login", method = RequestMethod.POST)
public String updateUI(Locale locale, Model model, @RequestParam("username") String username,
@RequestParam("hashedPW") String hashedPW, HttpServletRequest request) {
model.addAttribute("username", username);
user = new User(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
boolean loginResult = user.login();
if(loginResult == true) {
return "profile";
}
String output = "Failed login (" + username + ") requested, locale = " + locale;
log(output);
return "home";
}
以及我使用注入初始化模拟对象:
@Mock
private User mockUser;
@InjectMocks
private LoginController injectedLoginController;
@Before
public void setup() throws ServletException {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.standaloneSetup(injectedLoginController).build();
LOGFILE = new File("logs/general.log");
}
最后是单元测试:
@Test
public void testLoginSuccess() throws Exception {
String username = "Stefan";
byte[] hashedPW = "".getBytes();
when(mockUser.login()).thenReturn(true);
ResultActions ra = mvc
.perform(post("/Login").param("username", username).param("hashedPW", hashedPW.toString()))
.andExpect(status().isOk());
verify(mockUser).login();
}
总而言之,我希望控制器处理的 User 对象是 mockUser 类型而不是 User,并且 login() 方法被调用一次(相应地)并且 return "true".
但我得到的只是 “需要但未调用:mockUser.login() 实际上,与此模拟的交互为零。"
我很感激任何解决我问题的建议,因为我已经为此工作了很长一段时间,但我似乎没有找到窍门。
1) 这似乎是一个 IT 测试.. 你不应该强制任何注入.. spring 这样做:
@InjectMocks
private LoginController injectedLoginController;
2) 你需要监视你的控制器:
@SpyBean
private LoginController injectedLoginController;
3) 您需要将 User
的创建移动到控制器内的包级别方法:
user = createUser(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
...
User createUser(...){
return new User(username, username, hashedPW.getBytes(), LoginHandler.getInstance());
}
4) 使该方法 return 成为模拟用户:
doReturn(mockUser).when(injectedLoginController).createUser(...);
when(mockUser.login()).thenReturn(true);
如果您不想更改代码,可以从 PowerMockito
获得帮助,这有助于模拟构建新对象。
@RunWith(PowerMockRunner.class)
@PrepareForTest({User.class})
public class ControllerUnderTest {
// here User is class for which we want to mock object creation
}
现在,让我们模拟对象
@Test
public void testLoginSuccess() throws Exception {
....
User userMock = PowerMockito.createMock(User.class);
PowerMockito.whenNew(User.class).withArguments(username, username, hashedPW.getBytes(), LoginHandler.getInstance()).thenReturn(userMock);
expect(userMock.login()).andReturn(true);
verify(mockUser).login();
...
}
有关 powermock
的更多详细信息,请查看此 PowerMockito