Nuñito Calzada
Nuñito Calzada

Reputation: 2136

Spring Boot - MockMVC forwardedUrl using Thymeleaf

I have a basic SpringBoot app. using Spring Initializer, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.

I have this controller:

@Controller
@RequestMapping("/deviceevent")
public class DeviceEventController {

    @RequestMapping(value={ "/list"}, method = { RequestMethod.GET})
    public String deviceeventList()  {

        return "tdk/deviceEvent/DeviceEventList";
    }
}

and this other test class. Tests using Spring's MockMVC framework. This drives an MVC application in a test, as if it was running in a container,

    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    @WebMvcTest
    public class MockMvcTests {

        // Pull in the application context created by @ContextConfiguration
        @Autowired
        private WebApplicationContext wac;

        private MockMvc mockMvc;

        @Before
        public void setup() {
            // Setup MockMVC to use our Spring Configuration
            this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
        }


        @Test
        public void getDeviceEventsTest() throws Exception {
            this.mockMvc
                    .perform(get("/deviceevent/list") //
                            .accept(MediaType.parseMediaType("text/html;charset=UTF-8")))
                    .andExpect(status().isOk()) //
                    .andExpect(model().size(1)) //
.andExpect(forwardedUrl("tdk/deviceEvent/DeviceEventList"));
        }

But I got this error in the forwarded URL. I always used this method in JSP, never in Thymeleaf, but I guess that It is the same:

 java.lang.AssertionError: Forwarded URL expected:</tdk/deviceEvent/DeviceEventList> but was:<null>

Upvotes: 1

Views: 1193

Answers (2)

rhinds
rhinds

Reputation: 10043

Assuming a standard Thymeleaf/Spring setup, it looks like there is a misunderstanding with what the controller is doing - when the controller returns that string "tdk/deviceEvent/DeviceEventList" it is not forwarding the HTTP request somewhere, but returning a view name.

With a normal Spring-thymeleaf setup, that string corresponds to the name of a thymeleaf view that will be rendered on hitting that endpoint (I assume the controller is just serving a normal webpage - so that path probably corresponds to some file path most likely in src/main/resources - but again, this depends a lot on your spring config) - at this point the HTTP request has not been returned to the user, and Spring is still processing it - and will attempt to render the HTML view before returning to the user.

The forwarded URL is used if Spring is not rendering anything but instead returning a HTTP response to the user to forward them to another URL (which will start a different Spring request-response process) using a 301/302 mechanism.

Note the difference in the following methods:

@RequestMapping( value="/document", method=RequestMethod.GET )
public String newDocumentSettings( Model model ){
    model.addAllAttributes( contentManagementService.documentToJson() );
    return "pages/document-settings";
}


@RequestMapping( value="/document", method=RequestMethod.POST )
public String createNewDocument( @RequestParam String title, @RequestParam String overview, @RequestParam String tags ){
    Document doc = documentService.createDocument( title, overview, tags );
    return "redirect:/document/${doc.url}/1?getting-started";
}

The first renders the template at the given filepath, the second returns a redirect command to the browser to make another HTTP request to the given URL.

In any case, the forwardedUrl in your test case is because hte HTTP Response doesn't have a URL to forward to (because its returning the HTML). If you do want forwarding behaviour (e.g. you actually want to complete the response and the browser to make a second HTTP request) then you would likely need to update the controller as per example, however, if you are happy with the rendered html page, then the test is invalid (look at the Thymeleaf testing framework to see how to test templating).

Caveat: This is based on the assumption of default Spring-Boot config - if you have other config whereby that string does result in a forwarded HTTP request then this doesnt apply!

Upvotes: 1

riddle_me_this
riddle_me_this

Reputation: 9155

Taking a guess here, but the URL tdk/deviceEvent/DeviceEventList is probably not defined. Try replacing it with the URL associated with your context (edit as necessary):

    @Test
    public void getDeviceEventsTest() throws Exception {
        this.mockMvc
                .perform(get("/deviceevent/list")
                        .accept(MediaType.parseMediaType("text/html;charset=UTF-8")))
                .andExpect(status().isOk()) 
                .andExpect(model().size(1))
                .andExpect(forwardedUrl("/WEB-INF/tdk/deviceEvent/DeviceEventList.html"));
    }

Aside, instead of:

@RequestMapping(value={ "/list"}, method = { RequestMethod.GET})

you can use the shorthand:

@GetMapping("/list")

Upvotes: 0

Related Questions