Reputation: 2914
I started to use the new MVC Testframework of Spring 3.2 and got stuck with getting 406 HTTP Response Codes for all my test cases.
The testcase is plain simple
public class LocationResouceTest {
@Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
@Before
public void setup() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
@Test
public void testGetLocationByPlzPattern() throws Exception {
// here I need to define the media type as a static var from MediaType
this.mockMvc.perform(get("/someurl?someparam=somevalue")).andExpect(status().isOk());
}
}
the corresponding resource is
@Controller
// here I need to define the media type as string
@RequestMapping(value = "/someurl", produces = "application/json; charset=UTF-8")
public class LocationResource {
@ResponseBody
@RequestMapping(method = RequestMethod.GET)
public ArrayList<DTO> getAllIndex(@RequestParam("someparam") String param) {
return ... //the list of DTO classes is transformed to json just fine if called with curl
}
}
I am sure it is because of a wrong media type but I cannot figure out why.
The trace of the failing testcase:
java.lang.AssertionError: Status expected:<200> but was:<406> at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:60) at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:89) at org.springframework.test.web.servlet.result.StatusResultMatchers$5.match(StatusResultMatchers.java:546) at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:141) at de.yourdelivery.rest.location.LocationResouceTest.testGetLocationByPlzPattern(LocationResouceTest.java:37) 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.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28) 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.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88) 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)
Upvotes: 13
Views: 15141
Reputation: 3078
If you have a @Configuration
class, you can add the
@EnableWebMvc
annotation instead of using an XML configuration with <mvc:annotation-driven />
.
Upvotes: 21
Reputation: 2763
It is necessary to have both @EnableWebMvc
and .accept(MediaType.APPLICATION_JSON)
Upvotes: 4
Reputation: 21
You are missing the <mvc:annotation-driven />
in the configuration xml file.
Upvotes: 2
Reputation: 9
I met same problem just now, and is resolved by changing the return type of the Controller to String. And return a JSON string instead of an object directly. It does works.
Gson gson = new Gson();
return gson.toJson(dto);
Upvotes: -1
Reputation: 723
You need to add the following code to spring xml to serialize POJO in jackson.
<annotation-driven />
Upvotes: 3
Reputation: 23529
This might be caused by not having any MVC configuration in the Spring test context. When using something like:
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("/test-context.xml")
...then that test-context.xml
file should also include things like:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean id="..." class=
"org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
<bean id="..."
class="org.springframework.http.converter.StringHttpMessageConverter"/>
<bean id="..."
class="org.springframework.http.converter.FormHttpMessageConverter"/>
</mvc:message-converters>
</mvc:annotation-driven>
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="defaultContentType" value="application/json" />
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
</bean>
One can easily test if the above is the problem, with a controller that does not specify anything special (such as: no produces
in @RequestMapping
), and hence does not need any content negotiation:
@RequestMapping(value = "/foo", method = RequestMethod.GET)
@ResponseBody
public String getFoo() {
return "bar";
}
...with:
@Test
public void getFoo() throws Exception {
MvcResult result =
this.mockMvc.perform(get("/foo").accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andReturn();
Assert.assertEquals(result.getResponse().getContentAsString(), "bar");
}
Upvotes: 3
Reputation: 49915
I think the fix is to modify the request this way:
this.mockMvc.perform(get("/someurl?someparam=somevalue").contentType(MediaType.APPLICATION_JSON)).andExpect..
Upvotes: 0
Reputation: 47280
The spring way of doing things is, i think, to use a HttpResponseEntity, or return a modelandview. For example :
@ResponseBody
ResponseEntity<String> getFoo() {
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
String test = "{\"foo\":{\"title\": \"Stack\"}}";
return new ResponseEntity<String>(test, responseHeaders, HttpStatus.OK);
}
(I'd be interested in any further progress you make as its all pretty new)
Upvotes: 0