Unable to do post

776 views
Skip to first unread message

Russell Bateman

unread,
Mar 15, 2012, 8:28:46 PM3/15/12
to REST assured
I have a simple test for a Jersey/JAX-RS service I'm working on. The
following fails

@Test
public void testCreate()
{
expect()
.body( "name", equalTo( "Zzyzzx" ) )
.body( "code", equalTo( "zz" ) )
.when() .post ( "/language" );
}

yielding java.lang.AssertionError: XML path name doesn't match.
Expected: Zzyzzx
Actual: Apache Tomcat/6.0.32 - Error... (etc.)

But, if I post http://localhost:8000/myapp/v1/language with payload
{ "name":"Zzyzzx","code":"zz" }
it works fine, returning
Status: 201 Created
{"code","zz","id":"175","name":"Zzyzzx"}

Conversely, another test, testRead_byId(), works:

@Test
public void testRead_byId()
{
expect()
.body( "id", equalTo( "35" ) )
.body( "name", equalTo( "English" ) )
.body( "code", equalTo( "en" ) )
.when() .get( "/language/35" );
}

In case you care, I start with this:
@Before
public void setup()
{
RestAssured.baseURI = "http://localhost";
RestAssured.port = 8000;
RestAssured.basePath = "/myapp/v1";
}

I'm new to rest-assured; I would greatly appreciate a comment or two
if you can spare the time.

Thanks,
Russ

Johan Haleby

unread,
Mar 16, 2012, 2:49:02 AM3/16/12
to rest-a...@googlegroups.com
Hi Russ, 

Hi,

You may have confused "expect" with "given". You use expect().body(..) to setup expectations for the response body. If you want to send the name and code parameters with the request you can do like this:

given().
           param("name", "Zzyzzx").
           param("code", "zz").
when().
           post("/language");

Now imagine that "/language" is expected to return a JSON response body like this:
{
    "status": {
        "code": 1,
        "message": "OK"
    }
}

You can then make the request and verify the response like this:

given().
            param("name", "Zzyzzx").
            param("code", "zz").
expect().
            body("status.code", equalTo(1)).
            body("status.message", equalTo("OK")).
when().
           post("/language");

or slightly better:

given().
            param("name", "Zzyzzx").
            param("code", "zz").
expect().
            rootPath("status").
            body("code", equalTo(1)).
            body("message", equalTo("OK")).
when().
           post("/language");

I hope you get the idea.

Regards,
/Johan

Russell Bateman

unread,
Mar 16, 2012, 11:03:51 AM3/16/12
to rest-a...@googlegroups.com
(Embarrassed:) Ah, you're right. I had blindly followed an example to try out rest-assured quickly to see if it was something I'd like to do.

[Before I go on, let me thank you and the group here for being so prompt to help!]

Adopting your first example (without expects), it works, but I discovered that it never executes my restlet code. The create test therefore succeeds, but apparently because I'm not testing expectations yet. (If I do, it fails because it's never reaching the @POST code.)

In the read example I posted, that test does go through the @GET companion to this @POST code (which I give here though it might not be worth anything in this question):
@POST
@Consumes( { "application/json", "application/xml" } )
@Produces( { "application/json", "application/xml" } )
public Response create( Language language )
{
    try
    {
        language = languageBo.create( language );
    }
    {
        throw new WebApplicationException( Response.Status.INTERNAL_SERVER_ERROR );
    }

    URI createdUri = URI.create( "/myapp/v1/language/" + language.getId() );
    return Response.created( createdUri ).entity( language ).build();
}

What comes back in "real" execution is precisely {"code":"zz","id":"176","name":"Zzyzzx"}.

So, I want to have:
 given()
    .param( "name", equalTo( "Zzyzzx" ) )
    .param( "code", equalTo( "zz" ) )
.expect()
    .body  ( "name", equalTo( "Zzyzzx" ) )
    .body  ( "code", equalTo( "zz" ) )

.when() .post ( "/language" );
But, what do I do to make the post work (reach my restlet's @POST method)? Or, how can I see what the test is really trying to throw at it? Is there a logging statement I can throw in? All I can do is guess by what I set in the setup code plus the "/language" path. As I say, the read-by-id test works perfectly and is based on identical assumptions (as nearly as I understand anyway).

I greatly appreciate the help here.

Thank you,

Russ

Johan Haleby

unread,
Mar 18, 2012, 2:13:17 PM3/18/12
to rest-a...@googlegroups.com
You may have to specify content type since you're service seems to only accept application/json or application/xml.

given().contentType(ContentType.JSON). ..

or

given().contentType("application/json"). ..

/Johan

Russell Bateman

unread,
Mar 19, 2012, 10:13:52 AM3/19/12
to rest-a...@googlegroups.com
Thanks, Johan.

My code now stands thus:
@Test
public void testCreate()
{
//  given().contentType( ContentType.JSON )
    given().contentType( "application/json" )

           .param( "name", equalTo( "Zzyzzx" ) )
           .param( "code", equalTo( "zz" ) )
   .expect()//.rootPath( "" )

           .body ( "name", equalTo( "Zzyzzx" ) )
           .body ( "code", equalTo( "zz" ) )
   .when() .post ( "/language" );
}

@Test void testRead_byId()
{
    expect().body( "id",   equalTo( "35" ) )

            .body( "name", equalTo( "English" ) )
            .body( "code", equalTo( "en" ) )
   .when()  .get ( "language/35" );
}

JUnit testCreate() fails while JUnit testRead() succeeds. The in-line comments represent stuff I've tried as well.

Running my restlet in debug mode with breakpoints in both the @POST (create) and @GET/@Path( "/{id}" )  (read) methods, I stop only in the @GET, not in @POST. Also, I now get (from the POST attempt):

SEVERE: Servlet.service() for servlet Jersey REST Service threw exception
java.lang.Error: Error: could not match input
    at com.sun.jersey.json.impl.reader.JsonLexer.zzScanError( JsonLexer.java:491)
    ...


I do greatly appreciate the time you take to answer.

Russ

Johan Haleby

unread,
Mar 19, 2012, 10:30:21 AM3/19/12
to rest-a...@googlegroups.com
It seems like either you're sending the wrong data/parameters to your service or you're service is broken somehow. It's very hard to know what's going on and I think it's a bit out of scope of this mailing list. In the example you provided earlier the POST method accepts a Language object. Is that a DTO object? In the case I think you're sending the wrong data to the server. REST Assured makes it easy to serialize and send DTO objects, e.g. 

given().contentType(JSON).and().body(new Language(..)).when().post(..);

This will serialize the Language instance to JSON.

/Johan

Mircea Cocosila

unread,
Mar 19, 2012, 11:09:05 AM3/19/12
to rest-a...@googlegroups.com
Hi Russell,

Your problem is very likely as below.

You have:

@Test
public void testCreate()
{
//  given().contentType( ContentType.JSON )
    given().contentType( "application/json" )
           .param( "name", equalTo( "Zzyzzx" ) )
           .param( "code", equalTo( "zz" ) )

It should be:

@Test
public void testCreate()
{
//  given().contentType( ContentType.JSON )
    given().contentType( "application/json" )
           .param( "name", "Zzyzzx" )

           .param( "code", "zz" )

Get rid of the equalTo when you specify request parameter values. equalTo should be used to assert response elements only.

I hope this fixes your problem.

Cheers,
mircea
--
Mircea Cocosila
Developer
Architech Solutions
Mobile: +1 (416) 832-3035
m...@architech.ca
www.architech.ca

Russell Bateman

unread,
Mar 19, 2012, 11:58:21 AM3/19/12
to rest-a...@googlegroups.com
If so, I apologize for taking the mailing list's time.

I find that your latest suggestion does work! It's unclear as yet to me that it tests the response back from the server (I assumed .expect() and associated statements did that), but I'll be investigating and experimenting with it. This code produces a clean JUnit test run and now stops at my breakpoint inside my @POST method as I had hoped:
@Test
public void testCreate()
{
    Language language = new Language();

    language.setName( "Zzyzzx" );
    language.setCode( "zz" );

     given().contentType( "application/json" )
            .and()
            .body( language )
    .when().post( "/language" );
}
Now, as far as whether I should be asking these questions, note that my ReST service worked (and works) perfectly: I am only looking tentatively to REST Assured as a means to obtain greater code-path test coverage before giving up and telling my management and down-streamers that "it is what it is". Some on the team see REST Assured as superfluous and unnecessary. I like the concept and see REST Assured tests as a statement of what is correct and encompassing usage of our service. I've been laboring under the assumption that I just don't know how to use REST Assured and that, fixing this, I'll be happy with the result and that next time I'll write to REST Assured before implementing my service in the first place.

Profuse thanks, Johan, and also to Mircea who gave me some more information too,

Russ

Johan Haleby

unread,
Mar 19, 2012, 12:45:25 PM3/19/12
to rest-a...@googlegroups.com
Hi, 

I didn't mean to scare you away by what I said, I'm glad you're taking an interest in the project :). I just meant that questions regarding the way Jersey and HTTP requests works probably belongs in another forum, but feel free to ask whatever you like.

We use REST Assured a lot in my current project and it has saved us many times! I've also seen other projects exposing REST services that have found several bugs using REST Assured when they've started to validate the JSON response they actually send out. The payload doesn't always look like intended, especially when you use something like Jackson to serialize Java beans to JSON on server. But it's always a trade-off. 

Just a note on code coverage, it's hard to get code coverage if you're bootstrapping your Jersey application in a different JVM than your REST Assured tests. I think it should work with the Jacoco lib but I've never tried it myself. On the same JVM it shouldn't be any problems though. 

Regards,
/Johan

Russell Bateman

unread,
Mar 20, 2012, 8:22:38 PM3/20/12
to rest-a...@googlegroups.com
This question may be a little misguided. If so, please sneer, but give me a hint too so I can go to bed tonight less stupid than I awakened this morning.

:-)

In an effort to make my CRUD tests less brittle, I'd like to gather knowledge about what's inserted in my database by
@Entity
public class Thing

{
    @Id           (etc.)
    Integer id;

    int     x, y;
    ...
}

@Test
public void testCreate()
{
    Thing thing = new Thing();
    thing.x = 9;
    thing.y = 11;

    given().contentType( "application/json")
           .and()
           .body( thing )
   .when() .post( "http://localhost:8000/myapp/thing" );

}

When I get back from testCreate(), I need to know what thing.getId() would be. That way, I'll know, for example, of an object to update (PUT) and then delete (DELETE) safely in later JUnit tests.

So, I'm looking for some REST-assured construct to let me save that value. Does someone have some sample code?

Thanks for your patience,

Russ

Johan Haleby

unread,
Mar 26, 2012, 2:48:58 AM3/26/12
to rest-a...@googlegroups.com
Hi!

Sorry for the late response, I've on vacation. Assuming that the POST will return a response body containing a "Thing" then you can do like this:

@Test
public void testCreate()
{
    Thing thing = new Thing();
    thing.x = 9;
    thing.y = 11;

    Thing responseThing = given().contentType( "application/json")
           .and()
           .body( thing )
   .when() .post( "http://localhost:8000/myapp/thing" ).andReturn().body().as(Thing.class);

   String id = responseThing.getId();   
}

Regards,
/Johan

Russell Bateman

unread,
Mar 26, 2012, 8:23:26 AM3/26/12
to rest-a...@googlegroups.com
Thanks, Johan.

I have most or all of my answers now and am pretty happy with REST-assured. I have one of my services done and will go back in a finish the rest soon.

Thanks to this group for being very helpful.

Russ

Johan Haleby

unread,
Mar 26, 2012, 9:53:35 AM3/26/12
to rest-a...@googlegroups.com
Thanks. If you have any other comments or improvement suggestions please let us know.

Russell Bateman

unread,
Mar 26, 2012, 3:15:36 PM3/26/12
to rest-a...@googlegroups.com
Johan,

I've had time to look at this closer. It's exactly what I was looking for. In the meantime, however, the following is what I came up with that works perfectly and allowed me to buckle up my tests a week or so ago:

    /**
     * We expect that <tt>testCreate()</tt> above added a new language, "Zzyzzx" to the
     * database and that we can test deleting it.
     */
    @Test
    public void testDelete()
    {
        String json = get( "/language/name/Zzyzzx" ).asString();
        String id   = JsonPath.with( json ).get( "language.id" );

        expect().statusCode( 200 )
       .when()  .delete( "/language/" + id );
    }

I do love this stuff, though. It allows me, as I see it, three distinct advantages whatever anyone's objections to writing tests for this stuff might be:

It's...

- self-documenting.
- a statement of what URIs are supported/supposed to work.
- a statement of what HTTP Status Codes are returned/supposed to be returned.

Add these to your "marketing" on the REST-assured web page: In my book, that's TDD!

Best regards my friend, you rock!

Russ

Johan Haleby

unread,
Mar 26, 2012, 4:06:48 PM3/26/12
to rest-a...@googlegroups.com
Thanks a lot for your kind words, that's really what keeps one motivated to work on open source projects!

Regards,
/Johan
Reply all
Reply to author
Forward
0 new messages