java.lang.NullPointerException when i run my unit test - mockito

user1999453 picture user1999453 · Jan 23, 2013 · Viewed 9.4k times · Source

i have a spring mvc application and using mockito for my unit test.I keep getting null pointer exception when i run my unit test. :(

Please find below the method that my unit test will be based on:

@Controller
public class LogInController {

    @Autowired
    private MessageSource messageSource;

    @RequestMapping(method = RequestMethod.POST, value = "/login")
    public ModelAndView validateViewLogin(@ModelAttribute Person person,
    BindingResult result, HttpServletRequest request) {
        ModelAndView mav = new ModelAndView();

        String userName = person.getFirstName();
        String password = person.getPassword();
        boolean isUserValid = false;
        if (userName != null && password != null) {
             isUserValid = userManagerService.validateUserLogin(userName,password);
        }
        if (!isUserValid) {
            mav.addObject("failLog",messageSource.getMessage("login.user.fail", new String[] {"  ", "" }, request.getLocale()));
            mav.addObject("isUserValid", false);
            mav.setViewName("login");
            return mav;
        }
        mav.addObject("isUserValid", isUserValid);
        mav.setViewName("home");
        return mav;
    }

Please find below my unit test:

@Test
public void validateViewLogin_NotValidLogin_returnsLoginPage() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    Person person = new Person();
    person.setFirstName("John");
    person.setPassword("123");
    isUserValid = false;
    LogInController controller = new LogInController();

    // Inject mock user service into controller
    UserManagerService mockUserService =   mock(UserManagerService.class);
    controller.setUserManagerService(mockUserService);

    // Configure mock user service to accept the person
    when(mockUserService.validateUserLogin("John", "123")).thenReturn(
            isUserValid);

    // Attempt the validation
    ModelAndView mav = controller
            .validateViewLogin(person, result, request);

    // Check the result
    assertEquals("login", mav.getViewName());
}

Please find below stack trace error message when i run the unit test above:

java.lang.NullPointerException
at com.gemstone.presentation.LogInController.validateViewLogin(LogInController.java:99)
at com.gemstone.presentation.LogInControllerTest.validateViewLogin_NotValidLogin_returnsLoginPage(LogInControllerTest.java:79)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Basically line 99 which is shown below is returning null according to the stack trace:

mav.addObject(
                "failLog",
                messageSource.getMessage("login.user.fail", new String[] {
                        "  ", "" }, request.getLocale()));

Any ideas why i am getting the null pointer on this line please?However when i run my application it works fine and failLog message is displayed on my page.

thanks in advance.

Answer

nick.stuart picture nick.stuart · Jan 23, 2013

My guess is that your messageSource is null. You are mocking your request, but not your messageSource, and where you are newing the login controller it's not being injected by Spring.

While running it would get injected by Spring automatically.

Edit to clarify:

First you need to be able to set it some how for testing. You can either add a setter or a constructor to get to it, such as:

@Controller
public class LogInController {

    @Autowired
    private MessageSource messageSource;

    public void setMessageSource(MessageSource ms){
        this.messageSource = ms;
    }
    // ......
}

Then your test would need to change as well:

    @Test
public void validateViewLogin_NotValidLogin_returnsLoginPage() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    Person person = new Person();
    person.setFirstName("John");
    person.setPassword("123");
    isUserValid = false;
    LogInController controller = new LogInController();

    // Inject mock user service into controller
    UserManagerService mockUserService =   mock(UserManagerService.class);

            /** ADDED **/
            MessageSource ms = mock(MessageSource.class);

    controller.setUserManagerService(mockUserService);
            /** ADDED **/
            controller.setMessageSource(ms);

    // Configure mock user service to accept the person
    when(mockUserService.validateUserLogin("John", "123")).thenReturn(
            isUserValid);

            /** ADDED **/
            when(ms.getMessage("login.user.fail", new String[] {"  ", "" }).thenReturn("message");

    // Attempt the validation
    ModelAndView mav = controller
            .validateViewLogin(person, result, request);

    // Check the result
    assertEquals("login", mav.getViewName());
}

Just free handing this, so make sure I don't have any typos! Essentially you try the MessageSource as you did your UserManagerService, it's just an interface after all and can be mocked exactly the same way.