POST request fails (rest-assured test)

Purple picture Purple · Aug 13, 2015 · Viewed 25.6k times · Source

I have problem with making POST request with rest-assured.

This code works:

given().contentType(ContentType.JSON).body("{\"key\": \"val\"}").    
        when().post(url + resource).then().assertThat().statusCode(200).body("otherVal", equalTo(otherVal)); 

But I was trying to use param() or parameter() methods like that:

This one gives:

given().parameter("key", "val").                                      
        when().post(url + resource).then().assertThat().statusCode(200);  

Expected status code <200> doesn't match actual status code <415>.

This:

    given().parameter("key", "val").                                                         
            when().post(url + resource).then().assertThat().body("otherVal", equalTo(otherVal));  

java.lang.IllegalStateException: Expected response body to be verified as JSON, HTML or XML but no content-type was defined in the response. Try registering a default parser using: RestAssured.defaultParser(<parser type>);

And This:

RestAssured.defaultParser = Parser.JSON;                                                   
given().parameter("key", "val").                                                       
        when().post(url + resource).then().assertThat().body("otherVal", equalTo(otherVal));

java.lang.IllegalArgumentException: The JSON input text should neither be null nor empty

I have run out of ideas whats wrong.

What I'm trying to do is to avoid writing full jsons for all test, it will be faster if I could skip all "" and {}. Is my approach correct?

Answer

Johan picture Johan · Aug 15, 2015

Let's look at your first example:

given().contentType(ContentType.JSON).body("{\"key\": \"val\"}").    
        when().post(url + resource).then().assertThat().statusCode(200).body("otherVal", equalTo(otherVal));

What happens here is that you put { "key" : "val" } (as text) into the body of the request. This text happens to be JSON. From REST Assured's perspective you might as well could have put { "key" : "val" which is not valid JSON. Your server responds correctly since the server requires and understands JSON. It understands that the body should be JSON since you passing JSON as content-type.

So let's look at your second example:

given().parameter("key", "val").                                      
        when().post(url + resource).then().assertThat().statusCode(200);

Here your service returns 415 because you're missing the JSON content-type. What happens when you use param or parameter with POST is that you create form parameters. Form parameters are also sent in the request body BUT form parameters are not JSON! Specifying "key" and "val" as form parameter like you do will be the same as this:

given().contentType("x-www-form-urlencoded").body("key=val").when().url + resource).then().assertThat().statusCode(200);

So in your second example there's actually two problems:

  1. You're not sending JSON
  2. You have the wrong content-type

And because of (2) you get 415 from the server

Moving on to your third example:

given().parameter("key", "val").                                                         
            when().post(url + resource).then().assertThat().body("otherVal", equalTo(otherVal));

What (probably) happens here is that your server doesn't contain a response body because it expects the request to include "application/json" as content-type. So there is no body to assert (the request is wrong)! The response only contains the 415 status (line) as a header.

Which leads us to your last example:

RestAssured.defaultParser = Parser.JSON;                                                   
given().parameter("key", "val").                                                       
        when().post(url + resource).then().assertThat().body("otherVal", equalTo(otherVal));

Here you instruct REST Assured to treat a missing content-type as JSON but the problem (again) is that your server doesn't return any response body at all so this won't help.

Solution:

You should put a supported JSON object-mapping framework (Jackson, Faster Jackson, Simple JSON or Gson) in your classpath (for example jackson-databind) and just create a map as described in the documentation:

Map<String, Object>  jsonAsMap = new HashMap<>();
map.put("key", "val");

given().
        contentType(ContentType.JSON).
        body(jsonAsMap).
when().
        post(url + resource).
then().
        statusCode(200).
        body("otherVal", equalTo(otherVal)); 

Since creating maps in Java is quite verbose I usually do something like this if I have nested maps:

given().
        contentType(ContentType.JSON).
        body(new HashMap<String,Object>() {{
             put("key1, "val1");
             put("key2, "val2");
             put("key3", asList("val3", "val4"));
             put("nested", new HashMap<String,String>() {{
                 put("key4", "val4");
                 put("key5", "val5");
             }});
        }}).
when().
        post(url + resource).
then().
        statusCode(200).
        body("otherVal", equalTo(otherVal)); 

Or you create a DTO representation of your data and just pass an object to REST Assured:

MyDTO myDTO = new MyDTO(...);
given().
        contentType(ContentType.JSON).
        body(myDTO).
when().
        post(url + resource).
then().
        statusCode(200).
        body("otherVal", equalTo(otherVal)); 

You can read more in the object-mapping documentation.