Vincent Zheng
Vincent Zheng

Reputation: 122

Spring MVC test controller with spring security

I am trying to write the test case for test controller, here is the code for the controller

@Controller
@RequestMapping("/")
@SessionAttributes({"roles", "departments"})
public class AppController {

    @Autowired
    UserService userService;

    @Autowired
    RoleService roleService;

    @Autowired
    DepartmentService departmentService;

    @Autowired
    MessageSource messageSource;

    @Autowired
    PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices;

    @Autowired
    AuthenticationTrustResolver authenticationTrustResolver;

    static final Logger logger = LoggerFactory.getLogger(AppController.class);

    /**
     * This method will list all existing users.
     */
    @RequestMapping(value = { "/", "/list" }, method = RequestMethod.GET)
    public String listUsers(ModelMap model) {

        List<User> users = userService.findAllUsers();
        model.addAttribute("users", users);
        model.addAttribute("loggedinuser", getPrincipal());
        return "userslist";
    }
    /**
     * This method returns the principal[user-name] of logged-in user.
     */
    private String getPrincipal(){
        String userName = null;
        Object principal = getCurrentUser();

        if (principal instanceof UserDetails) {
            userName = ((UserDetails)principal).getUsername();
        } else {
            userName = principal.toString();
        }
        return userName;
    }

    private Object getCurrentUser(){
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }
//The rest part of the controller}

I using TestNG based on the this tutorial: http://websystique.com/springmvc/spring-4-mvc-and-hibernate4-integration-testing-example-using-annotations/, and currently I have following in my test cases:

//all the import file
public class AppControllerTest {

    @Mock
    UserService userService;

    @Mock
    MessageSource message;

    @InjectMocks
    AppController appController;

    @Spy
    List<User> users = new ArrayList<User>();

    @Spy
    ModelMap model;

    @Mock
    BindingResult result;

    @BeforeClass
    public void setUp(){
        MockitoAnnotations.initMocks(this);
        users = getUsers();
    }

    private List<User> getUsers() {
        // TODO Auto-generated method stub
        User u1 = new User();
        u1.setId(1);
        u1.setFirstName("Admin");
        u1.setLastName("Admin");
        u1.setUsername("admin");
        u1.setEmail("[email protected]");
        u1.setDateOfBirth(new LocalDate());
        u1.setPassword("admin");
        Department admin = new Department();
        admin.setId(1);
        admin.setName("Admin");
        admin.setDescription("Admin");
        u1.setDepartment(admin);
        Role adminRole = new Role();
        adminRole.setId(1);
        adminRole.setRoleName("ADMIN");
        Set<Role> roles = new HashSet<>();
        roles.add(adminRole);
        u1.setRoles(roles);

        User u2 = new User();
        u2.setId(1);
        u2.setFirstName("Alice");
        u2.setLastName("Lin");
        u2.setUsername("alice.lin");
        u2.setEmail("[email protected]");
        u2.setDateOfBirth(new LocalDate());
        u2.setPassword("Alice0102");
        u2.setDepartment(admin);
        u2.setRoles(roles);

        users.add(u1);
        users.add(u2);
        return users;
    }

    @Test
    public void listUsers(){
        when(userService.findAllUsers()).thenReturn(users);
        Assert.assertEquals(appController.listUsers(model), "userslist");
        Assert.assertEquals(model.get("users"), users);
        verify(userService, atLeastOnce()).findAllUsers();
    }
}

Now the question is, if I didn't comment this line model.addAttribute("loggedinuser", getPrincipal()); in my controller class, when I run maven test, it will throw null pointer exception, that is obvious, since in my test cases I didn't login to the application. What can I do so I can make the test passed include this line?

Upvotes: 0

Views: 373

Answers (1)

starman1979
starman1979

Reputation: 1084

You should refactor your code so that the getCurrentUser() calls live in a separate class. You should keep those separate anyway because most likely other controllers will need to make the same calls. But for this context, you need to refactor because you cannot mock private method calls (at least not using Mockito).

Once the user related calls are in a separate class, you can mock it just as you have done the other services above, using @Mock annotation.

Upvotes: 1

Related Questions