3

I'm working from the Spring documentation here. My goal is to use the "webAppContextSetup" option to test my spring configuration along with my controller, but I'm having trouble getting the controller's methods mapped inside of the TestDispatcherServlet. So far I have the following setup:

Data Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd">

    <jpa:repositories base-package="com.example.test"/>

    <bean id="contractManagementDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        <property name="dataSource" ref="contractManagementDataSource"/>
        <!-- no need for the load time weaving from the spring documentation since we're using hibernate as our JPA provider -->
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"/>

    <context:property-placeholder location="classpath:config.properties"/>

</beans>

MVC Configuration

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd

         http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.example.test"/>

    <mvc:annotation-driven />

    <mvc:resources mapping="/public/**" location="/public/"/>

    <mvc:view-controller path="/" view-name="contract-management"/>

    <bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

ContractController.java

@RestController
@RequestMapping("contract")
public class ContractController {
    @Autowired
    DaoService daoService;

    @RequestMapping("{name}")
    public Contract getContract(@PathVariable String name) {
        return daoService.findContract(name);
    }
}

ContractControllerIT.java

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("/spring/*.xml")
public class ContractControllerIT {
    @Autowired
    private WebApplicationContext webApplicationContext;

    private MockMvc mockMvc;

    @Before
    public void setup() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
    }

    @Test
    public void getAccount() throws Exception {
        mockMvc.perform(get("contract/{name}", "test")).andExpect(status().isOk());
    }

}

At the moment I see the following error when I run the test:

java.lang.AssertionError: Status expected:<200> but was:<404>

And, interestingly, some seemingly contradictory logging statements before the failure.

10:38:09.410 [main] INFO  org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/mvc.xml]
10:38:09.469 [main] INFO  org.springframework.web.context.support.GenericWebApplicationContext - Refreshing org.springframework.web.context.support.GenericWebApplicationContext@7a4ccb53: startup date [Mon Sep 28 10:38:09 MDT 2015]; parent: org.springframework.web.context.support.GenericWebApplicationContext@4a22f9e2
10:38:09.607 [main] INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/contract/{name}]}" onto public com.example.test.domain.Contract com.example.test.controller.ContractController.getContract(java.lang.String) throws java.lang.Exception
10:38:09.786 [main] INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@7a4ccb53: startup date [Mon Sep 28 10:38:09 MDT 2015]; parent: org.springframework.web.context.support.GenericWebApplicationContext@4a22f9e2
10:38:09.813 [main] INFO  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter - Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@7a4ccb53: startup date [Mon Sep 28 10:38:09 MDT 2015]; parent: org.springframework.web.context.support.GenericWebApplicationContext@4a22f9e2
10:38:09.840 [main] INFO  org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Mapped URL path [/public/**] onto handler 'org.springframework.web.servlet.resource.ResourceHttpRequestHandler#0'
10:38:09.844 [main] INFO  org.springframework.web.servlet.handler.SimpleUrlHandlerMapping - Root mapping to handler of type [class org.springframework.web.servlet.mvc.ParameterizableViewController]
10:38:09.889 [main] INFO  org.springframework.mock.web.MockServletContext - Initializing Spring FrameworkServlet ''
10:38:09.889 [main] INFO  org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization started
10:38:09.898 [main] INFO  org.springframework.test.web.servlet.TestDispatcherServlet - FrameworkServlet '': initialization completed in 9 ms
10:38:09.912 [main] WARN  org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [contract/test] in DispatcherServlet with name ''

Also, this is my first question on StackOverflow, so any advice on how to improve my question is both welcome and appreciated.

  • FYI: `web.xml` is completely irrelevant for any discussion involving `MockMvc` since the Spring MVC Test Framework runs outside of a Servlet container. – Sam Brannen Oct 01 '15 at 15:57
  • I thought that might be the case, but my intent was to show how I was breaking up my configuration files in case there was some problem there. Do you think I should remove it from the question? –  Oct 01 '15 at 17:50
  • Yes, I would remove the `web.xml` listing since it can only serve to confuse people. – Sam Brannen Oct 01 '15 at 18:18

2 Answers2

5
10:38:09.912 [main] WARN  org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [contract/test] in DispatcherServlet with name ''

That last part says it all: there is no mapping for "contract/test" which is what you perform a GET request for in your integration test.

However, there is nothing wrong with the mappings in your controller. The only issue is that you omitted the leading slash in your integration test.

The following solves your problem:

@Test
public void getAccount() throws Exception {
    mockMvc.perform(get("/contract/{name}", "test"))
           .andExpect(status().isOk());
}

Pay special attention to the leading slash in "/contract/{name}".

Regards,

Sam (author of the Spring TestContext Framework)

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • Ah, I had the feeling the problem might be some silly mistake like this. Thanks for taking the time to write such a thorough answer. –  Oct 01 '15 at 17:48
0

I see a couple things different than what I usually do for my controller & tests:

ContractController.java - add a slash for both request mappings:

@RequestMapping("/contract")
...
@RequestMapping("/{name}")

ContractControllerIT.java - add a slash:

@Test
public void getAccount() throws Exception {
    mockMvc.perform(get("/contract/{name}", "test")).andExpect(status().isOk());
}
Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
Ann Addicks
  • 915
  • 12
  • 25
  • That fixed it. I'd be curious to see what you find out regarding making this unnecessary. Definitely a frustrating problem considering the spring docs don't mention it. –  Sep 29 '15 at 18:00
  • @jpack I'm glad it helped. It's frustrating when the docs aren't correct or omit something. I tried looking yesterday why the server is needed, but didn't come up with anything. I was going to delete this post today since I hadn't responded. – Ann Addicks Sep 29 '15 at 18:24
  • The scheme and server name are not required. – Sam Brannen Sep 29 '15 at 22:16
  • Although leading slashes in `@RequestMapping` paths are recommended for greater clarity, they are not required. – Sam Brannen Sep 29 '15 at 22:25
  • @SamBrannen Hey Thanks! I've changed it in my code too. One of the hazards of cloning a repository and running with their example. – Ann Addicks Sep 30 '15 at 00:31