如何将数据模型模拟注入 spring 控制器?

How to inject data-model mock into spring controller?

在我的单元测试中,我需要在 spring @Controller 中模拟一个数据模型实例,否则 @RequestMapping 方法的 return 值会出错。

为此,我尝试了以下操作:

  1. 创建一个用户模拟,它调用 user.login() 并且需要 return "true"
  2. 在 LoginController 中注入模拟对象
  3. 将登录方法存根为 return true
  4. 执行POST/使用来自spring测试的MockMVC登录
  5. 验证 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