Unit testing REST Controller with spring-test-mvc
Asked Answered
A

2

10

I have upgraded my Spring dependency to Spring 3.1.1.RELEASE and I'm trying to use spring-test-mvc to unit test a simple Controller. I have been following the technique used in Spring REST Controller Test with spring-test-mvc framework, since it seems to have worked for that person, but I have been unsuccessful so far. I think there's some key configuration I"m missing in my test context file.

I get no errors. The reason I know it's not working is because Hello World never gets printed (see Controller). What am I missing here?

Controller:

@Controller
@RequestMapping("/debug")
public class DebugOutputController {

    @RequestMapping(method = RequestMethod.POST)
    public void saveDebugOutput(@RequestBody DebugOutput debugOutput, HttpServletResponse response) {
        System.out.println("Hello World");
    }
}

Test Class:

@RunWith(SpringJUnit4ClassRunner.class) //this lets tests access Spring beans defined in the context config file
@ContextConfiguration(locations={"file:src/test/resources/itest/restAPITestContext.xml"}) //tells the test where to get configuration and beans to be used by the test.
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,  TransactionalTestExecutionListener.class}) //overrides the default stack of listeners
public class ITRestAPI{

@Autowired
private DebugOutputController debugOutputController;

private MockMvc mockMvc;

@Before
public void setUp() throws Exception {
    mockMvc = MockMvcBuilders.standaloneSetup(debugOutputController).build();
}

@After
public void tearDown() throws Exception {
}

@Test
public void shouldPerformPost() throws Exception {
    this.mockMvc.perform(post("/debug"));
}
}

restAPITestContext.xml:

<?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/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-3.1.xsd">

    <mvc:annotation-driven />
    <mvc:default-servlet-handler />
    <context:component-scan resource-pattern="*DebugOutputController*" base-package="com.company.project.servlet" />    

</beans>
Amieva answered 17/5, 2012 at 13:22 Comment(2)
spring-test-mvc is really promising, but lacks documentation. Are you aware of anything but the README at this point?Cavorilievo
@MikePartridge All the information I've found about it is from their Github site.Amieva
A
20

Turns out an HttpMessageNotReadable exception was occurring and I just couldn't see it because I was not logging or printing it anywhere. I found it by building the HTTP request in my test class using the DefaultRequestBuilder class and adding a andDo(print()) :

DefaultRequestBuilder requestBuilder = MockMvcRequestBuilders.post("/debug").contentType(MediaType.APPLICATION_JSON).body(new String("{\"T1\":109.1, \"T2\":99.3}").getBytes());
this.mockMvc.perform(requestBuilder).andDo(print());

So after that, using the output of andDo(print()), I could see that the HttpMessageNotReadable exception was being thrown, but didn't know the details of the exception or what was causing it. To see the details, I had to add this to the controller class to write the exception details to the response body:

@ExceptionHandler(HttpMessageNotReadableException.class)
@ResponseBody
public String handleException1(HttpMessageNotReadableException ex)
{
    return ex.getMessage();
}

This revealed the following exception:

Could not read JSON: Unrecognized field "T1" (Class com.company.project.model.device.DebugOutput), not marked as ignorable

which I fixed by adding the @JsonProperty annotation to the setters in my model class:

@JsonProperty("T1")
public void setT1(Float t1) {
    T1 = t1;
}
Amieva answered 18/5, 2012 at 14:26 Comment(3)
What's this "print()" method you mentioned? Can't find it in the test class and since your test class doesn't extend from anywhere, can you point out where you got it from or what it does. Thanks.Berna
@MathiasLin I use the print() method by importing it statically, like so: import static org.springframework.test.web.server.result.MockMvcResultHandlers.print; This method prints details about the request being sent. It is VERY useful in debugging.Amieva
should point out that the actual import statement is as follows: import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.printArette
O
1

I just spent ages trying to debug similar issue, and my solution was to do the following:

MvcResult mvcResult = mockMvc
        .perform(
                post("/api/v1/admin/licences/{licenceId}/dataAccess", licenceId)
                        .header(POLLEN_INTERNAL, configuration.getPollenInternalSecret())
                        .header(USER_TYPE, USER_TYPE_OWNER)
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(mapper.writeValueAsString(productHierarchyBeans)))
        .andReturn();

This mvcResult variable would contain all the errors or response entities. Then you can analyse it in the debugger or using something like mvcResult.getResponse() or mvcResult.getResolvedException() to find out what works or doesn't, and then fix the issue.

Odysseus answered 6/6, 2022 at 15:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.