Spring controller testing with Mockito

2019-07-23 04:22发布

问题:

I'm trying to test My Spring controllers using Mockito, but I can't actually get how can I do that without making everything @Mock. Moreover test method returns me NullPointerException, as it can see no user and actually no user role at all. Is there a way to test my controllers somehow?

(Controller class)

@Controller
@SessionAttributes("user")
@RequestMapping("/login.htm")
public class LoginController{

    @Autowired
    private UserDao userDao;
    @Autowired
    private LoginValidator loginValidator;

    public LoginValidator getLoginValidator() {
        return loginValidator;
    }

    public void setLoginValidator(LoginValidator loginValidator) {
        this.loginValidator = loginValidator;
    }

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @RequestMapping(method = RequestMethod.GET)
    public String getSendEmptyForm(ModelMap model, HttpServletRequest req) {
        req.getSession().invalidate();
        model.addAttribute("loginForm", new LoginForm());
        return "login";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String postSubmittedForm(ModelMap model, @ModelAttribute("loginForm") LoginForm loginForm,
            BindingResult result, SessionStatus status) {
        //validate form
        loginValidator.validate(loginForm, result);

        if (result.hasErrors()) {
            return "login";
        } 

        User user = userDao.findByLogin(loginForm.getLogin());
        model.addAttribute("user", user);

        if (user.getRole().getName().equals("Admin")) {
            model.addAttribute("usersList", userDao.findAll());
            return "viewAllUsersPage";
        } 

        if (user.getRole().getName().equals("User")){
            return "userPage";
        }

        model.addAttribute("error", "Your role is not User or Admin");
        return "errorPage";
    }

}

And my testing class

@RunWith(MockitoJUnitRunner.class) 
public class LoginControllerTest {

    @InjectMocks
    private LoginController controllerUT = new LoginController();

    @Mock
    private ModelMap model;

    @Mock
    private LoginForm loginForm;

    @Mock
    private BindingResult result;

    @Mock
    private SessionStatus status;

    @Mock
    private LoginValidator validator;

    @Mock
    private UserDao userDao;

    @Mock
    private User useк;

    @Test
    public void testSendRedirect(){
        final String target = "login";
        String nextPage = controllerUT.postSubmittedForm(model, loginForm, result, status);
        assertEquals(target, nextPage);
    }

}

回答1:

First off you seem to be missing stubbing for loginForm.getLogin() and userDao.findByLogin(loginForm.getLogin()) and user.getRole().getName(). Without such stubbing, these methods called on a mock will return a default value (i.e. null).

So you may want to add :

when(loginForm.getLogin()).thenReturn(login);
when(userDao.findByLogin(login)).thenReturn(user);
when(user.getRole()).thenReturn(role);
when(role.getName()).thenReturn("Admin");

You will want to vary the return values for different tests. Depending on your implementation classes for User and Role, you could simply supply real instances. For a test that simulates the result to have errors you'll want to add this stubbing :

when(result.hasErrors()).thenReturn(true);

since otherwise the default false is returned.