Sending XML Request with REST-assured
Learn to send requests with XML request body and validate the response using REST-assured APIs.
1. By Parsing Custom Object to XML
1.1. Parsing Java Object to XML
If we can take the help of libraries such as Jackson or JAXB, for converting a custom object into XML String, it should be the preferred way. It helps in making the code more readable and manageable.
For example, if we have an API that takes the following request:
HTTP POST /users lokesh admin@howtodoinjava.com male active
We can create a Java object UserObject and annotate it with Jackson annotations so that it parses to the desired XML string.
@JacksonXmlRootElement(localName = "user") class UserObject
Now, we can use Jackson’s XmlMapper.writeValueAsString() method to get the XML string for the UserDTO instance. This will generate the XML as given previously.
UserObject newUser = new UserObject(); newUser.setName("lokesh"); newUser.setEmail("admin@howtodoinjava.com"); newUser.setGender("male"); newUser.setStatus("active"); String newUserXml = new XmlMapper().writeValueAsString(newUser);
1.2. Sending Request with REST-assured
Sending the XML request body is very simple. We need to set the content type to “application/xml” (or any other custom mediatype, if any) and then pass the XML string to the given().body(xml) method.
A complete example of sending the XML request to a POST API is:
@Test public void createUserWithJSONObject_thenSuccess() throws JSONException, JsonProcessingException < UserObject newUser = new UserObject(); newUser.setName("lokesh"); newUser.setEmail("admin@howtodoinjava.com"); newUser.setGender("male"); newUser.setStatus("active"); String newUserXml = new XmlMapper().writeValueAsString(newUser); given() .body(newUserXml) .contentType("application/xml") .queryParam("access-token", "xxxx") .when() .post("/users") .then() .statusCode(201) .body("id", notNullValue()) .body("name", equalTo("lokesh")) .body("gender", equalTo("male")) .body("status", equalTo("active")) .body("email", equalTo("admin@howtodoinjava.com")) .log().all(); >
2. By Reading XML from File
In automation testing, we may not have created DTO objects, and we may need to rely on hardcoded XML strings. In this case, creating such XML strings in separate XML files that are API specific is always better.
We read the XML file for each API and post its content to the API. In this approach, only obtaining the XML string is different and invoking the API is the same as the previous example.
File newUserXmlFile = new File("src/test/resources/requests/newUser.xml"); given() .body(newUserXmlFile) . .when() .post("/users") .then() . ;
This short tutorial taught us to pass the XML requests to REST APIs using REST-assured. We learned to get the XML request strings from either custom Java objects or XML files in the filesystem. Then we learned to pass the XML to the API and validate the response.
Extract XML values with RestAssured
In the last chapter, we have learned how to parse the JSON response and get values from the JSON response. Sometimes we need to extract values from the XML files, Having XML response is an old-style but still some applications use (which are not migrated to JSON).
We are using the XML endpoint at : https://chercher.tech/sample/api/books.xml
We have to use a bath to get specific or arrays of values from the XML. I have listed a few below which may help you to understand things a little better.
- root.parent.child.grandchild : get all grandchild element from the XML.
- root.parant.child[0].grandchild : get a grand child which is present under first child (child[0]), here we use 0 as the array start from 0.
- root.parent[0] : Extract the specific value we can also extract XML which is present under a tag, If you don’t use the index then RestAssured will fresh all the matching elements and extract the example from them
- [email protected] == ‘spcific value’ : We can extract the attributes of other child elements when one of the child element has a specific value.
- ** : It is not possible all the time to write a complete XML a path to a specific item in such cases we can use ** to match any parent.
Extract the first match
Lets try to get a specific element from the XML response. We have to use the extract() method to get the value from the response, the response could be XML or it could be JSON.
path() method gets a value from the response body using the JsonPath or XmlPath syntax. REST Assured will automatically determine whether to use JsonPath or XmlPath based on the content-type of the response.
import org.junit.jupiter.api.Test; import io.restassured.RestAssured; public class XMLValuesExtraction < @Test void sampleTest() < String book = RestAssured.given().when() .get("https://chercher.tech/sample/api/books.xml") .then().extract().path("bookstore.book.title"); System.out.println(book); >>
Get Nodes based on a specific value:
we can get the values of the sibling/child elements based on a specific element. For example, consider, you want to retrieve the name of the author whose books are in a specific category, then we can use this kind of XML path.
@Test void sampleTest() < String book = RestAssured.given().when() .get("https://chercher.tech/sample/api/books.xml") .then().extract().path("bookstore.book.findAll < [email protected] == 'cooking' >.year"); System.out.println(book); >
Deep Search:
You can use the deep search when you are not sure of the part elements but you know only the values, then in such cases, you can use the deep search. A deep search will find all the matching elements irrespective of their location in the XML.
This also helps in reducing the length of the XML path that we were writing.
@Test void sampleTest() < String book = RestAssured.given().when() .get("https://chercher.tech/sample/api/books.xml") .then().extract().path("**.findAll < [email protected] == 'cooking' >.year"); System.out.println(book); >
When there is more than one matching element then you may see the below error.
java.lang.ClassCastException: io.restassured.internal.path.xml.NodeChildrenImpl cannot be cast to java.base/java.lang.String
In such cases, please use the Type as NodeChildrenImpl instead of String
@Test void sampleTest() < NodeChildrenImpl book = RestAssured.given().when() .get("https://chercher.tech/sample/api/books.xml") .then().extract().path("bookstore.book.findAll < i[email protected] == 'cooking' >.price"); System.out.println(book); >
Extract all the matches from XML | NodeChildrenImpl
Similar to extracting a single value we can extract multiple values as well. To extract multiple values we have to use another class called NodeChildrenImpl.
The output ( returned as a single string)
The NightingaleHarry Potter
Important Methods from NodeChildrenImpl:
Using NodeChildrenImpl class, we can perform other operations on the examine extracted. I have listed a few below.
- get(int index): Get the value of the specific index
- size(): Which is the size of the retrieved elements
- isEmpty(): Checks whether anything got retrieved or zero elements present; if 0 elements present it will return true otherwise false
- list(): By default, NodeChildrenImpl returns a string format but when we use list we can get the output in list format
import org.junit.jupiter.api.Test; import io.restassured.RestAssured; import io.restassured.internal.path.xml.NodeChildrenImpl; public class XMLValuesExtraction < @Test void sampleTest() < NodeChildrenImpl books = RestAssured.given().when() .get("https://chercher.tech/sample/api/books.xml") .then().extract().path("bookstore.book.title"); System.out.println("just single string: "+ books); System.out.println("spcific index : "+ books.get(0)); System.out.println("is empty : "+ books.isEmpty()); System.out.println("size : "+ books.size()); System.out.println("list : "+ books.list()); >>
The output
NodeChildrenImpl on a subset of XML:
In the above program, we have seen how to use NodeChildrenImpl for simple operations; now let’s apply this class on a subset of XML.
When we try to fetch a subset of XML, we might need to extract the values from the subset of XML. In such cases, we can use NodeChildrenImpl
Example to fetch the subset of XML:
Don’t be scared that in the above output we have not received the subset XML; we have received subset XML but the NodeChildrenImpl class only has given the values present in the subset XML because NodeChildrenImpl class handle tags internally.
So we will be performing operations on the first match (Please read the program as well for better understanding).
- name(): Gets the tag name of the element(subset)
- attributes(): fetches all attributes present in the subset XML of the parent element
- getAttribute(): Fetches the value for the given attribute from the root element of the subset XML
- get(): Gets the value present in the given tag name.
- children(): All the tags present in the root tag of the subset
- children().get(index): Gets the element based on the index inside the subset of XML
- getNode(): Fetches another subset of XML, on which we can perform the above mentioned all operations