Search This Blog

About Me

Chennai, TamilNadu, India
I am a craftsperson by passion and quality assurer by profession. I love to build - a craft or a piece of code. I look for reusability in crafts and usability in APPs. I always find something new to create and something new to learn. I believe in liberal world, flexible schedules and boundaryless place . https://automationautomated.blogspot.com/p/am-into-software-test-automation-and-r.html

Sunday, November 10, 2019

BBD way of REST API Automation RestAssured

API automation #REST-assured


API what is it, why does it matter?

  • Application Program Interfaces define the business logic of the application being designed.
  • Functional rules - User interaction with services and data.
  • Endpoints -  Relay information through request-response protocols (typically through a ‘Uniform Resource Identifier’) and gives response.
  • SOAP and REST APIs
API Calls interaction



  Why I have to test API ?


Complete coverage! UI testing, either manual or automated, is still just testing the presentation layers to make sure the end user experience matches expectations and meets functional requirements.

If solely using UI testing without data-driven API testing, it’s nearly impossible to validate all different possibilities of how a user could manipulate the application and the related sub-applications. The best approach is to test both UI and API.

Early testing helping shift left.

API testers on a QA team will focus on all the different permutations and scenarios to validate within a single API and among multiple APIs involved in a chain of requests (end-to-end).

Could test APIs without having complete knowledge of the entire system.

Business depend on APIs working 100% as expected hence simpler and more convenient way is to run tests through a series of checks after a build and they’re generally straightforward to create, automate, and maintain.

Quick validation.  API automated testing often takes a fraction of the time as UI automated tests. 

The bottom line is that API testing offers many advantages to a more traditional manual or automated UI approach. With the right tools and expertise it can supplement existing testing efforts and contribute added capabilities to your testing goals.


How can I test these APIs?

Some Tools....
SoapUI
Postman
Katalon Studio
Tricentis Tosca
Apigee
Jmeter
Swagger Inspector
Assertible
RESTClient
Rest Assured
Rest template
HTTP
Restsharp
RestConsole 
Chakram
Karate
SuperTest

...

Testing a web site REST api - Json using developer tools and Postman

Sample APIs site -- Kayak.com  resqres.in











API Automation – REST Assured

Authorization


CSRF
OAuth1
OAuth2
Headers
Cookies

Sample Code::

public void basicAuthorization() {
REQUEST.auth()
  .basic("user1", "user1Pass")
  .when()
    .get("http://localhost:8080/spring-security-rest-basic-auth/api/foos/1")
  .then()
    .assertThat()
    .statusCode(200)
  .getHeader("Date");



public class OAuth2Test {
    private String resourceOwnerLogin(String tokenUri, String clientId, String clientSecret, String username, String password, String scope) {
       
      Response response = REQUEST.auth().preemptive().basic(clientId, clientSecret)   
                        .formParam("grant_type", "password")
                        .formParam("username", username)
                        .formParam("password", password)
                        .formParam("scope", scope)
                        .when()
                        .post(tokenUri);
       
        String accessToken = response.path("access_token").toString();
        String tokenType = response.path("token_type").toString();
       
        String dateHeader  = response.getHeader("Expires");
        String respCookie = response.getCookie("auth_cookie");
        return accessToken;
    }
}

Assertions -Json Gpath and Hamcrest Matchers



{
    "page": 1,
    "per_page": 3,
    "total": 12,
    "total_pages": 4,
    "data": [
        {
            "id": 1,
            "email": "george.bluth@reqres.in",
            "first_name": "George",
            "last_name": "Bluth",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/calebogden/128.jpg"
        },
        {
            "id": 2,
            "email": "janet.weaver@reqres.in",
            "first_name": "Janet",
            "last_name": "Weaver",
            "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
        },
        ...
    ]
}
    



public RequestSpecification REQUEST;
@Before
public void details() {
RestAssured.baseURI = "https://reqres.in/api";
REQUEST = RestAssured.given().contentType(ContentType.JSON);
}

@Test
public void shouldFetchListOfAllUsers() {
REQUEST
.get("/users")
.then()
.assertThat()
.statusCode(200)
.contentType(ContentType.JSON)
.statusLine(containsString("OK"))
.log().body()
.body("data", hasSize(3))
.body("data[0].id", equalTo(1))
.body("data[0].first_name", equalTo("George"))
.body("data.findAll{it.id>2}.first_name", hasItem("Emma"))
.body("data[0].last_name", not(equalTo("George")));
}

Reference #


Matchers


Hamcrest comes with a library of useful matchers. Here are some of the most important ones.
Core
  • anything 
  • describedAs
  • Logical
    • allOf 
    • anyOf 
    • not 
  • Object
    • equalTo 
    • hasToString 
    • instanceOfisCompatibleType 
    • notNullValuenullValue 
    • sameInstance 
  • Beans
    • hasProperty 
  • Collections
    • array 
    • hasEntryhasKeyhasValue 
    • hasItemhasItems 
    • hasItemInArray
  • Number
    • closeTo 
    • greaterThangreaterThanOrEqualTolessThanlessThanOrEqualTo 
  • Text
    • equalToIgnoringCase 
    • equalToIgnoringWhiteSpace 
    • containsStringendsWithstartsWith 


import org.hamcrest.Description;

import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
public class IsNotANumber extends TypeSafeMatcher
@Override
public boolean matchesSafely(Double number) {
  return number.isNaN(); 
  } 
public void describeTo(Description description) { 
  description.appendText("not a number"); 
  } 
public static Matcher notANumber() {
  return new IsNotANumber(); 
  } 
}
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.examples.tutorial.IsNotANumber.notANumber;

public class NumberTest {
@Test
public void testSquareRootOfMinusOneIsNotANumber() {
  assertThat(Math.sqrt(-1), is(notANumber()));
   }
}



Static JSON comparison JsonSchemaValidator



user2.json in classpath
{
    "data": {
        "id": 2,
        "email": "janet.weaver@reqres.in",
        "first_name": "Janet",
        "last_name": "Weaver",
        "avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
    }
}

import static io.restassured.module.jsv.JsonSchemaValidator.matchesJsonSchemaInClasspath;
import static org.hamcrest.MatcherAssert.assertThat;
@Test
public void shouldGetSecondUserVerifyUsingJsonSchemaValidator() {
String json = REQUEST.get("/users/2").body().asString();
REQUEST.get("/users/2")
  .then()
  .statusCode(200)
  .time(lessThan(2L), TimeUnit.SECONDS)
  .log().all();
assertThat(json, matchesJsonSchemaInClasspath("user2.json"));
}

POJOs



Plain Old Java Object not bound by any restriction other than those forced by the Java Language Specification

SingleUserResponse.java


@JsonPropertyOrder({ "data" })
public class SingleUserResponse {
@JsonProperty("data")
private Data data;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
@JsonProperty("data")
public Data getData() {
   return data;
}
@JsonProperty("data")
public void setData(Data data) {
   this.data = data;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
   return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
  this.additionalProperties.put(name, value);
} }


Data.java

@JsonPropertyOrder({ "id", "email", "first_name", "last_name", "avatar" })
public class Data {

@JsonProperty("id")
private Integer id;
@JsonProperty("email")
private String email;
@JsonProperty("first_name")
private String firstName;
@JsonProperty("last_name")
private String lastName;
@JsonProperty("avatar")
private String avatar;
@JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();

@JsonProperty("id")
public Integer getId() {
  return id;
}
@JsonProperty("id")
public void setId(Integer id) {
  this.id = id;
}
@JsonProperty("email")
public String getEmail() {
  return email;
}
__

@JsonProperty("email")
public void setEmail(String email) {
  this.email = email;
}
@JsonProperty("first_name")
public String getFirstName() {
  return firstName;
}
@JsonProperty("first_name")
public void setFirstName(String firstName) {
  this.firstName = firstName;
}
@JsonProperty("last_name")
public String getLastName() {
return lastName;
}
@JsonProperty("last_name")
public void setLastName(String lastName) {
  this.lastName = lastName;
}
@JsonProperty("avatar")
public String getAvatar() {
  return avatar;
}
@JsonProperty("avatar")
public void setAvatar(String avatar) {
  this.avatar = avatar;
}
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
  return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
  this.additionalProperties.put(name, value);
}
}

(De-)Serialize and validate instantly with POJOs


Serializable, has a no-argument constructor, and allows access to properties using getter and setter methods 

@Test
public void shouldGetFirstUserAndVerifyFirstNameAndId() {
SingleUserResponse usersResponse = REQUEST
  .get("/users/1")
  .as(SingleUserResponse.class);
Assert.assertEquals(Integer.valueOf(1), usersResponse.getData().getId());
Assert.assertEquals("George", usersResponse.getData().getFirstName());

}

@Test
public void shouldGetSecondUserAndVerifyFirstNameAndId() {
SingleUserResponse usersResponse = REQUEST
  .get("/users/2")
  .as(SingleUserResponse.class);
Assert.assertEquals(Integer.valueOf(2), usersResponse.getData().getId());
Assert.assertEquals("Janet", usersResponse.getData().getFirstName());


}




POJO and JSON Requests



Request
{
    "name": "morpheus",
    "job": "zion "
}
POJOs created as
CreateUserRequest.java
Response
{
  "name": "morpheus",
  "job": "zion ",
  "id": "836",
  "createdAt": "2019-07-29T08:56:32.044Z"
}
POJOs created as
UsersResponse.java




@Test
public void createUser() {
HashMap<String, String> jsonAsMap = new HashMap<String,String>();
  jsonAsMap.put("name", "John");
  jsonAsMap.put("job", "qa");
REQUEST.contentType(ContentType.JSON)
    .body(jsonAsMap)
  .post("/users")
  .then()
    .assertThat()
    .statusCode(201)
    .contentType(ContentType.JSON)
    .statusLine(containsString("Created"))
    .log().body();
}
@Test
public void createUserWithPojo() {
CreateUserRequest request = new CreateUserRequest();
  request.setName("Jane");
  request.setJob("UX");
UsersResponse usersResponse = REQUEST.contentType(ContentType.JSON)
    .body(request)
  .post("/users")
     .as(UsersResponse.class);
id = usersResponse.id;
System.out.println(usersResponse.id);
}


Multi part form Data

When sending larger amount of data to the server it's common to use the multipart form data technique. Rest Assured provide methods called multiPart that allows you to specify a file, byte-array, input stream or text to upload. 

REQUEST
  .multiPart(new File("/home/johan/some_large_file.bin"))
.expect()
  .body("fileUploadResult", is("OK"))
.when()
  .post("/fileUpload");



InputStream in = new FileSystemResource("49MB_Size_Image.jpg").getInputStream();
REQUEST
  .multiPart("file3", "file_name.bin", in)
  .formParam("name", "value")
.expect()
  .body("fileUploadResult", is("OK"))
.when()
  .post("/advancedFileUpload");