I am new to rest assured automation framework, so need help. I have to automate a simple API wherein I send the request in body.
given().log().all().contentType("application/json").body(payload).when().log().all().post("THE
POST URL").then().log().all().assertThat().statusCode(200);
I have to read the request from json file, and I am able to read the request from the .json file successfully. But I want to parameterize the values, and unable to understand on how to parameterize the file. Following is the sample .json file:
{
"id" : 5,
"name" : "Harry"
}
I do not want to hardcode the values of id and name here, but instead parameterize them using data providers or any other method. Any pointers on the same would be helpful.
A good practice for API testing using Rest-Assured is POJO approach. It helps you avoid manipulating json file (one kind of hardcode)
Step 1: You define a POJO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#AllArgsConstructor
#NoArgsConstructor
public class Person {
private int id;
private String name;
}
I use lombok for generating verbose code.
Step 2: Create Data-provider method
#DataProvider(name = "create")
public Iterator<Person> createData() {
Person p1 = new Person(1, "Json");
Person p2 = new Person(2, "James");
Person p3 = new Person(3, "Harry");
return Arrays.asList(p1,p2,p3).iterator();
}
Step 3: Write test
#Test(dataProvider = "create" )
void test1(Person person) {
given().log().all().contentType(JSON)
.body(person)
.post("YOUR_URL")
.then().log().all().assertThat().statusCode(200);
}
You need to add 2 lib into your project classpath to make above code work.
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
Related
Controller
#RequestMapping(value="/create", method=RequestMethod.POST, consumes={"application/json"})
public Alien addDetails(#RequestBody Alien alien){
return repo.save(alien);
}
Alien.java
#Entity
public class Alien{
#Id
private int id;
private String name;
private String planet;
Getter and setter
Now I want to validate the post json data before saving it to the database.
If any of the field is empty then the controller should return an error.
For example
{"id": 1, "name": "Alien1", "planet":"Mars" }
This is acceptable json data
But if there is any field is missing such as
{"name": "Alien1", "planet":"Mars" }
Then the controller should return an error and not creating the instance of Alien
I tried with #Valid #NotNull still the controller creates an empty instance of Alien and save to the database.
First of all, you should add spring boot validation dependency into your pom.xml :
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
And then add annotations like #NotBlank or #NotEmpty to verify if a value is assigned to that field or not. Those annotations should be on the top of your entity's attributes which you want to validate them.For example:
public class Alien{
#NotBlank(message="Name is mandatory")
private String name;
}
Finally, add #Valid annotation into your controller method like:
public Alien addDetails(#Valid #RequestBody Alien alien){
return repo.save(alien);
}
You should add #Validated annotation to your controller to make #Valid annotation do the work. You should also annotate fields in your Entity with constraints.
I.e., name could be #NotBlank.
And yes, make sure that you have imported:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Edit:
In Controller, you should not accept Entity as a body. Instead, you should create a DTO object and then map it to the Entity in the service. You should also put validation constraints on the DTO!
I hava an Spring Boot Rest Api
#RestController
public class BookController {
#Autowired
private BookRepository bookRepo;
#GetMapping(value = "/library/", produces ={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
public List<Book> index(){
Iterable<Book> bookIterable = bookRepo.findAll();
List<Book> bookList = new ArrayList<>();
bookIterable.forEach(a->bookList.add(a));
return bookList;
}
My Homework is to add an additonal data representation so that when i put in the request i should can choose between which data representation i won't XML or JSON
Problem is
I get even json how can i change between XML and Json when i do a get Request to the Endpoint
To solve your problem you need to use the Accept header. more details
The Content Type header indicates the type of data that you pass in the request. more details
You need to make a request with the header, if you want to send and receive xml:
Accept: application/xml;
Content-Type: application/xml;
usefull link
Ok now i found it my self what you need to know in order to use an XML output is first add
to the pom.xml file following dependencies: Jackson XML Dataformat
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
Then i just have to Add Jackson Annotations to my Entity Class
#Entity
#JacksonXmlRootElement
public class Book {
public Book() {
}
#JacksonXmlProperty(isAttribute = true)
#Id
#GeneratedValue
private Integer id;
#JacksonXmlProperty(isAttribute = true)
private String title;
#JacksonXmlProperty(isAttribute = true)
private Integer numberOfCopies;
Thats it then i can Make a request with the Accept Header value application/xml
I'm trying to get JSON data from REST resource and automatically convert it to Java Object with JSON-to-Java binding. I use Jersey framework 2.21 with jersey-media-moxy module as JSON provider in my client application .
I cannot figure it out why I get null instead of proper Post object when I do this:
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://www.travelportland.com/wp-json");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => null
The Post class implementation looks like this (at this point I only want to take 'title' field from JSON):
#XmlRootElement
public class Post {
private String title;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
All works fine when I try to get String:
String postStr = response.readEntity(String.class);
or if I try to get some other resource:
WebTarget webTarget = client.target("http://jsonplaceholder.typicode.com");
Response response = webTarget.path("posts/9").request().get();
Post post = response.readEntity(Post.class); // => com.example.Post#74e28667
It seems like this issue somehow related to structure or size of JSON data.
How can I solve this problem?
Seems to be an issue with MOXy. I am not sure what (maybe size, bad chars, I don't know). But I tested with Jackson, and it works fine. You might want to just make the switch Jackson if you can't figure it out with MOXy.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey2.version}</version>
</dependency>
You will need to set the class annotation to ignore unknown properties if you are not model all the keys from the JSON
#XmlRootElement
#JsonIgnoreProperties(ignoreUnknown = true)
public class Post {
Jackson will also recognize most JAXB annotations if you already using a bunch of them.
Also make sure you get rid of the the MOXy dependency of you will have to explicitly register JacksonFeature.class with the Client, to disable the MOXy provider.
I'm trying to develop a REST service using Apache-CXF, on top of JAX-RS. For starters, I have a method called test that receives a String message and int value. I want the clients to be able to pass these parameters in a POST message body. I can't seem to achieve this.
Before I paste the code here, here are some details:
I'm using CXF without Spring
It's not a web app, so I don't have the WEB-INF folder with the web.xml
I test the service using SoapUI and Postman (Google Chrome application)
With the following code, I get WARNING: javax.ws.rs.BadRequestException: HTTP 400 Bad Request:
DemoService.java
#WebService(targetNamespace = "http://demoservice.com")
#Path("/demoService")
public interface DemoService {
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String test (String message, int value);
}
DemoServiceImpl.java
public class DemoServiceImpl implements DemoService {
#Override
public String test(String message, int value) {
return "test message: " + message + " value = : " + value;
}
}
DemoServer.java
public class DemoServer{
public static void main(String[] args) {
JAXRSServerFactoryBean serverFactory = new JAXRSServerFactoryBean();
DemoService demoService = new DemoServiceImpl();
serverFactory.setServiceBean(demoService);
serverFactory.setAddress("http://localhost:9090");
serverFactory.create();
}
}
My POM.xml (minus the attributes in the root tag, everything's there)
<project ...>
<modelVersion>4.0.0</modelVersion>
<groupId>demo</groupId>
<artifactId>demoService</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<cxf.version>3.0.0</cxf.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxrs</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>${cxf.version}</version>
</dependency>
<!-- Jetty is needed if you're are not using the CXFServlet -->
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>${cxf.version}</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-service-description</artifactId>
<version>3.0.0-milestone1</version>
</dependency>
</dependencies>
</project>
Testing with {"message":"hello there!", "value":"50"} to the URL http://localhost:9090/demoService/test gave a HTTP 400 Bad Reuest.
Then I saw this question on S.O.: How to access parameters in a RESTful POST method and tried this:
added the following nested class in DemoServer.java:
#XmlRootElement
public static class TestRequest {
private String message;
private int value;
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public int getValue() { return value; }
public void setValue(int value) { this.value = value; }
}
I also modified the DemoService interface and the implementation to use this class as a parameter in the test method, although this is still ultimately not what I want to do. (just showing the implementation here, question's already getting long):
#Override
public String test(TestRequest testRequest) {
String message = testRequest.getMessage();
int value = testRequest.getValue();
return "test message: " + message + " value = : " + value;
}
And to fix this error that I got: SEVERE: No message body reader has been found for class DemoService$TestRequest, ContentType: application/json (in Postman I see error 415 - unsupported media type) I added the following dependencies (jettison and another thing) to the POM.xml:
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-rs-extension-providers</artifactId>
<version>2.6.0</version>
</dependency>
I tested the service using the following JSON message, in a HTTP POST request:
{"testRequest":{"message":"hello there!", "value":"50"}}
This works. Though this solution where I use a TestRequest class to encapsulate the parameters works, that's not the solution I'm looking for. I want to be able to pass the two parameters in a JSON message, without having to introduce this TestRequest class (explicitly).
Questions:
Would this be easier to implement using Jersey?
I don't have a web.xml nor a WEB-INF folder, so I can't configure CXF in a cxf.xml file can I? A lot of tutorials online seem ot use a lot of XML configuration, but I don't want to deploy a framework like TomEE or Spring or Glassfish just to do that.
Searching online for solutions, I came across Spring Boot. Would you recommend using that, perhaps? Would that make developing web services like this easier?
Also, how do I get it to return the value in JSON format (or is it not supposed to do that for Strings?)
My friend pointed me to this stack exchange question: JAX-RS Post multiple objects
and also the following documentation: http://cxf.apache.org/docs/jax-rs-and-jax-ws.html
which states:
public class CustomerService {
public void doIt(String a, String b) {...};
}
By default JAX-RS may not be able to handle such methods as it
requires that only a single parameter can be available in a signature
that is not annotated by one of the JAX-RS annotations like
#PathParam. So if a 'String a' parameter can be mapped to a #Path
template variable or one of the query segments then this signature
won't need to be changed :
#Path("/customers/{a}")
public class CustomerService {
public void doIt(#PathParam("a") String a, String b) {...};
}
So, to answer my question, NO, it cannot be done.
The following example works in a Java EE6 (Glassfish3) project of mine but failed after I switched to Java EE7 (Glassfish4). The HTTP request returns "500 Internal Error" without any message in the Glassfish server log. The project was setup using NetBeans8 as Maven Web Project and has no special dependencies, beans.xml or other configuration.
#RequestScoped
#Path("generic")
public class GenericResource {
#GET
#Path("ping")
#Produces(APPLICATION_JSON)
public List<String> debugPing() {
return Arrays.asList("pong");
}
And then:
$ curl -v http://localhost:8080/mavenproject2/webresources/generic/ping
> GET /mavenproject2/webresources/generic/ping HTTP/1.1
...
< HTTP/1.1 500 Internal Server Error
As as I understand, all REST handling is done by the Jackson reference implementation and that Jackson uses Jersey as underlaying JSON library. One of the two is supposed to have some kind of provider for all basic data types. Only custom made classes need a self written ObjectMapper. Are these concepts still correct?
It took me some hours but I finally solved this question myself.
First fact is that the Glassfish4 JAX-RS implementation "Jersey" as switched its underlying JSON library from Jackson 1.x to Eclipselink MOXy. The latter seems not be able to convert Lists, Arrays and arbitrary POJOs to JSON out of the box. Therefore I tried to force JAX-RS to use Jackson 2.x and disable MOXy.
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
// This is Jackson 2.x, Jackson 1.x used org.codehaus.jackson!
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
#ApplicationPath("rest")
public class RestConfig extends Application {
private final static Logger log = LoggerFactory.getLogger(RestConfig.class);
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<>();
log.info("Enabling custom Jackson JSON provider");
set.add(new JacksonJsonProvider() /* optionally add .configure(SerializationFeature.INDENT_OUTPUT, true) */);
return set;
}
#Override
public Map<String, Object> getProperties() {
Map<String, Object> map = new HashMap<>();
log.info("Disabling MOXy JSON provider");
map.put("jersey.config.disableMoxyJson.server", true);
return map;
}
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new java.util.HashSet<>();
// ... add your own REST enabled classes here ...
return resources;
}
}
My pom.xml contains:
<dependency>
<!-- REST (Jackson as JSON mapper) -->
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
<dependency>
<!-- REST (Jackson LowerCaseWithUnderscoresStrategy etc.) -->
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency>
Hope this helps someone!