I have the following code in one of my controllers:
@Controller
@RequestMapping("/preference")
public class PreferenceController {
@RequestMapping(method = RequestMethod.GET, produces = "text/html")
public String preference() {
return "preference";
}
}
I am simply trying to test it using Spring MVC test as follows:
@ContextConfiguration
@WebAppConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class PreferenceControllerTest {
@Autowired
private WebApplicationContext ctx;
private MockMvc mockMvc;
@Before
public void setup() {
mockMvc = webAppContextSetup(ctx).build();
}
@Test
public void circularViewPathIssue() throws Exception {
mockMvc.perform(get("/preference"))
.andDo(print());
}
}
I am getting the following exception:
Circular view path [preference]: would dispatch back to the current handler URL [/preference] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
What I find strange is that it works fine when I load the "full" context configuration that includes the template and view resolvers as shown below:
<bean class="org.thymeleaf.templateresolver.ServletContextTemplateResolver" id="webTemplateResolver">
<property name="prefix" value="WEB-INF/web-templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
<property name="characterEncoding" value="UTF-8" />
<property name="order" value="2" />
<property name="cacheable" value="false" />
</bean>
I am well aware that the prefix added by the template resolver ensures that there are not "circular view path" when the app uses this template resolver.
But then how I am supposed to test my app using Spring MVC test? Has anyone got any clue?
This has nothing to do with Spring MVC testing.
When you don't declare a
ViewResolver
, Spring registers a defaultInternalResourceViewResolver
which creates instances ofJstlView
for rendering theView
.The
JstlView
class extendsInternalResourceView
which isBold is mine. In otherwords, the view, before rendering, will try to get a
RequestDispatcher
to which toforward()
. Before doing this it checks the followingwhere
path
is the view name, what you returned from the@Controller
. In this example, that ispreference
. The variableuri
holds the uri of the request being handled, which is/context/preference
.The code above realizes that if you were to forward to
/context/preference
, the same servlet (since the same handled the previous) would handle the request and you would go into an endless loop.When you declare a
ThymeleafViewResolver
and aServletContextTemplateResolver
with a specificprefix
andsuffix
, it builds theView
differently, giving it a path likeThymeleafView
instances locate the file relative to theServletContext
path by using aServletContextResourceResolver
which eventually
This gets a resource that is relative to the
ServletContext
path. It can then use theTemplateEngine
to generate the HTML. There's no way an endless loop can happen here.When using
@Controller
annotation, you need@RequestMapping
and@ResponseBody
annotations. Try again after adding annotation@ResponseBody