mike.l
mike.l

Reputation: 219

How to test Spring MVC controller with RedirectAttributes

I am using:

I am trying to write a junit test case for a controller with RedirectAttributes.

Controller's signature is:

@RequestMapping(method = RequestMethod.POST)
public String homePOST(@Validated({ IBasic.class }) @ModelAttribute("userCommand") User userCommand,
            BindingResult result, Model model,
            RedirectAttributes flashAttributes)

JUnit test case is:

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = { "/root-context.xml", "/servlet-context.xml" })
public class HomeControllerTest {

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    @Test
    public void testHomePOST() {
        MockHttpServletRequest request = new MockHttpServletRequest("POST", "/");
        request.addParameter("username", "user1");
        request.addParameter("password", "mypasswd");

        MockHttpServletResponse response = new MockHttpServletResponse();

        Object handler;

        try {
            handler = handlerMapping.getHandler(request).getHandler();

            ModelAndView modelAndView = handlerAdapter.handle(request,
                    response, handler);

            assertViewName(modelAndView, "redirect:/myview");
        } catch (Exception e) {
            String err = "Error executing controller : " + e.toString();

            fail(err);
        }
    }
}

When handlerAdapter.handle() is executed, i am getting :

java.lang.AssertionError: Error executing controller : java.lang.NullPointerException
at org.junit.Assert.fail(Assert.java:93)
at com.myapps.service.impl.test.HomeControllerTest.testHomePOST(HomeControllerTest.java:75)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174)
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)

If i remove the RedirectAttributes from the controller, it is working properly.

Can anyone please provide some help how to test a controller with RedirectAttributes?

Thank you in advance.

In order to simulate this, here is the controller code:

@Controller
@RequestMapping(value = "/")
public class HomeController {
    @RequestMapping(method = RequestMethod.POST)
    public String homePOST(
            @Validated({ IBasic.class }) @ModelAttribute("userCommand") User userCommand,
            BindingResult result, Model model,
            RedirectAttributes flashAttributes) {

        return "redirect:/myview";
    }
}

Upvotes: 2

Views: 7847

Answers (2)

Orchid
Orchid

Reputation: 223

Use mockito framework to mock it. use

RedirectAttributes flashAttributes;
flashAttributes=Mockito.mock(RedirectAttributes.class)

in the setup function.

Upvotes: 0

Biju Kunjummen
Biju Kunjummen

Reputation: 49935

I am able to replicate your issue - the underlying reason for the issue is that if a RedirectAttributes is present as a handler method parameter, then just before returning from the handler adapter, the output "flashmap" is pulled and the flashAttributes added to it. Now, this output flashMap is retrieved from the httprequest - in your case, this is null for your MockHttpServletRequest.

The fix is to simply set a dummy output flashMap in the MockHttpServletRequest:

request.setAttribute(DispatcherServlet.OUTPUT_FLASH_MAP_ATTRIBUTE,new FlashMap());

This works for me now. Can you please try and see if it fixes this issue for you.

Upvotes: 5

Related Questions