Testing RESTful Web Services can be cumbersome because you have to deal with low-level concerns which can make your tests verbose, hard to read and to maintain. Fortunately, there are libraries and best practices helping you to keep your integration tests concise, clean, decoupled and maintainable. This post covers those best practices.
Use Reusable RequestSpecifications
Create a RequestSpecification to reuse request configurations (base URL, parameters, content type, debugging logging) that you want to use for all requests.
private static RequestSpecification spec; @BeforeClass public static void initSpec(){ spec = new RequestSpecBuilder() .setContentType(ContentType.JSON) .setBaseUri("http://localhost:8080/") .addFilter(new ResponseLoggingFilter()) .addFilter(new RequestLoggingFilter()) .build(); } @Test public void useSpec(){ given() .spec(spec) .param("limit", 20) .when() .get("blogs") .then() .statusCode(200); }
Use POJOs and Object Mapping
Don’t fiddle with string concatenation or the cumbersome JsonObject to create JSON for a request or to check the responded JSON. This is verbose, not typesafe and error-prone.
Instead, create a separate POJO class and let an ObjectMapper like Jackson do the deserialization and serialization for you. Rest-assured provides built-in support for object mapping.
BlogDTO retrievedBlog = given() .spec(spec) .when() .get(locationHeader) .then() .statusCode(200) .extract().as(BlogDTO.class);
Use Fluent Setters for POJOs
Don’t use ordinary setters (verbose) or constructors with huge argument lists (hard to read and error-prone) to create test request payload. Instead, use fluent setters.
public class BlogDTO { private String name; private String description; private String url; //let your IDE generate the getters and fluent setters for your: public BlogDTO setName(String name) { this.name = name; return this; } public BlogDTO setDescription(String description) { this.description = description; return this; } public BlogDTO setUrl(String url) { this.url = url; return this; } // getter... }
IntelliJ IDEA generates those setters for you. Moreover, there is a nice shortcut. Just write the fields (like private String name;
) and hit Alt+Insert
, Arrow-Down
until “Getter and Setter”, Enter
, Select “Builder” for “Setter template”, then Shift+Arrow-Down
(multiple times) and finally press Enter
. This is so cool!
Afterwards, you can use the fluent setters to write readable and typesafe code for creating test data.
<pre>BlogDTO newBlog = new BlogDTO() .setName("Example") .setDescription("Example") .setUrl("www.blogdomain.de");</pre>
Since object mapping is supported by rest-assured, we can just pass the object to rest-assured. Rest-assured will serialize the object and set the JSON in the request body.
String locationHeader = given() .spec(spec) .body(newBlog) .when() .post("blogs") .then() .statusCode(201) .extract().header("location");
Use AssertJ to Check the Returned POJOs
AssertJ is an awesome library to write fluent, readable and typesafe test assertions. I like it much more than Hamcrest, because AssertJ guides you to find the fitting matcher for the current type.
You can use AssertJ to check the returned (and deserialized) POJO in the response body.
Use AssertJ’s isEqualToIgnoringGivenFields()
Usually, you want to check whether the retrieved bean is equal to the send bean that you wanted to create. Using a normal equals()
-check doesn’t work because the service has generated an ID for the new entity. Hence, the beans differ in the ID fields. Fortunately, you can tell AssertJ to ignore certain fields during an equals-check. This way you don’t have to check every field manually.
assertThat(retrievedEntity).isEqualToIgnoringGivenFields(expectedEntity, "id");
That’s all for today, don’t hesitate to contact us in case of some questions!