Tuesday, June 2, 2020

Webservices API Testing with Rest Assured Api and Validating JSON by Karate Framework

Rest-Assured is a Java-based library that is used to test RESTful Web Services..
This enables us to test a wide variety of Request combinations and in turn test different combinations of core business logic. Rest-Assured library also provides the ability to validate the HTTP Responses received from the server.
Karate is an open-source general-purpose test-automation framework that can script calls to HTTP end-points and assert that the JSON or XML responses are as expected. 
Karate is built on top of Cucumber, another BDD testing framework, and shares some of the same concepts. One of these is the use of a Gherkin file, which describes the tested feature. However, unlike Cucumber, tests aren't written in Java and are fully described in the Gherkin file.
A Gherkin file is saved with the “.feature” extension. It begins with the Feature keyword, followed by the feature name on the same line. It also contains different test scenarios, each beginning with the keyword Scenario and consisting of multiple steps with the keywords GivenWhenThenAnd, and But.
Maven Dependencies:
<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-apache</artifactId>
<version>0.6.0</version>
</dependency>
We'll also need the karate-junit4 dependency to facilitate JUnit testing:

<dependency>
<groupId>com.intuit.karate</groupId>
<artifactId>karate-junit4</artifactId>
<version>0.6.0</version>
</dependency>

Creating Tests

We'll start by writing tests for some common scenarios in a Gherkin Feature file.

Testing the Status Code

Let's write a scenario that tests a GET endpoint and checks if it returns a 200 (OK) HTTP status code:

Testing the Response

Let's a write another scenario that tests that the REST endpoint returns a specific response.

The match operation is used for the validation where ‘$' represents the response. So the above scenario checks that the response exactly matches ‘{id:”1234″,name:”John Smith”}'.
We can also check specifically for the value of the id field:

The match operation can also be used to check if the response contains certain fields. This is helpful when only certain fields need to be checked or when not all response fields are known:

Validating Response Values With Markers


In the case where we don't know the exact value that is returned, we can still validate the value using markers — placeholders for matching fields in the response.

For example, we can use a marker to indicate whether we expect a null value or not:

  • #null
  • #notnull
Or we can use a marker to match a certain type of value in a field:

  • #boolean
  • #number
  • #string
Other markers are available for when we expect a field to contain a JSON object or array:

  • #array
  • #object
And there're markers for matching on a certain format or regular expression and one that evaluates a boolean expression:

  • #uuid — value conforms to the UUID format
  • #regex STR — value matches the regular expression STR
  • #? EXPR — asserts that the JavaScript expression EXPR evaluates to true
Finally, if we don't want any kind of check on a field, we can use the #ignore marker.

Running Tests

Now that the test scenarios are complete, we can run our tests by integrating Karate with JUnit.
We'll use the @CucumberOptions annotation to specify the exact location of the Feature files:

@RunWith(Karate.class)
@CucumberOptions(features = "classpath:karate")
public class KarateUnitTest {
//...
} 


Scenario:
Assert following scenario.

Scenario:
{
"testCodesInfo": [
{
"testMasterInformation": {
"testCode": "004556",
"testName": "hCG,Beta Subunit,Qual,Serum",
"orderable": true,
"specialityCode": null,
"procedureClassCode": "RI",
"procedureClassDesc": "RI-RIA",
"testType": "Test",
"publishedCode": "Published",
"labSystemCode": "LCLS",
"labSpecificInfo": [
]
},
"statTestCode": {
"testNumber": "004556",
"testName": "HCG, Qual (Pregnancy)",
"regionCode": "A",
"regionName": "Atlantic",
"active": true
},
"statLocalized": false,
"statApplicableTest": true,
"testMasterExists": true
},
{
"testMasterInformation": {
"testCode": "004515",
"testName": "Estradiol",
"orderable": true,
"specialityCode": null,
"procedureClassCode": "RI",
"procedureClassDesc": "RI-RIA",
"testType": "Panel",
"publishedCode": "Published",
"labSystemCode": "LCLS",
"labSpecificInfo": [
]
},
"statTestCode": {
"testNumber": "004515",
"testName": "Estradiol",
"regionCode": "A",
"regionName": "Atlantic",
"active": true
},
"statLocalized": false,
"statApplicableTest": true,
"testMasterExists": true
}
]
}

Karate framework needs a feature  file "acceptance criteria", Karate runner java file and karate.config file.

labcorp.feature
Feature: sample karate test script
Background:
* url baseUrl
Scenario: get users and validate
Given path 'labcorp'
When method get
Then status 200
And match response contains { message : '#ignore', user : '#object' }
* def userContent = response.user
#* configure report = true
And match userContent contains { userId : '#string', name : '#string', location : '#string', startDate: '#number' }
And match userContent contains { endDate : '#number', managerId : '#string', ciscoFinesseConfig : '#object' }
And match userContent contains { skills : '#array' , allUserSpecificSettings: '#object', effectiveUiFeatures : '#array' }
And match userContent contains { effectiveSettings : '#object' }
And match userContent.ciscoFinesseConfig contains { agentId: '#notnull', extension: '#notnull', primaryServer : '#notnull', secondaryServer: '#notnull'}
* def userSpecificSettings = userContent.allUserSpecificSettings
And match userSpecificSettings contains { "com.lca.phoenix.lcc.ui.feature.cutOffTimeSettings" : '#string'}
And match userSpecificSettings contains { "com.lca.phoenix.lcc.ui.setting.showAltLegend" : '#string' }
And match userSpecificSettings contains { "com.lca.phoenix.lcc.ui.setting.useAmazonConnect" : '#string' }
And match userSpecificSettings contains { "com.lca.phoenix.lcc.ui.feature.labResults" : '#string' }
And match userSpecificSettings contains { "com.lca.phoenix.lcc.ui.feature.divisonalCutOffTimeSettings" : '#string' }


karate.config example
function() {
var env = karate.env; // get system property 'karate.env'
karate.log('karate.env system property was:', env);
// karate.configure('connectTimeout', 5000);
// karate.configure('readTimeout', 5000);
var port = karate.properties['demo.server.port'];
karate.log("port is: " + port);
if (!port) {
port = env == 'web' ? 8090 : 8080;
}
var protocol = 'http';
var config = { baseUrl: protocol + '://localhost:' + port };
return config;

another karate.config example
function fn() {
var env = karate.env; // get java system property 'karate.env'
karate.log('karate.env selected enviroment was:', env);
Karate.configure("ssl", true)
if (!env) {
env = 'dev'; // a custom 'intelligent' default
} ////env can be anything: dev, qa, staging
var config = { // base config JSON
env: env,
appId: 'manoj',
appSecret: 'adhi',
baseUrl: 'https://labcorp' + env + ' org'/',
};
if (env == 'dev') {
config.appId: 'devmanoj'
config.appSecret: 'mypass'
// over-ride only those that need to be
// config.baseUrlDev = 'https://wsa.labcorp.com/dev';
} else if (env == 'qa') {
config.appId: 'qamanoj'
config.appSecret: 'mypass'
// config.baseUrlQA = 'https://wsa.labcorp.com/qa';
}
karate.log("My host: "+ config.baseUrl);
//or
//karate.log("Prod URL being used: "+ config.baseUrl);
//karate.log("Dev URL being used: "+ config.baseUrlQA);
//karate.log("QA URL being used: "+ config.baseUrlDev);
// don't waste time waiting for a connection or if servers don't respond within 5 seconds
karate.configure('connectTimeout', 5000);
karate.configure('readTimeout', 5000);
return config;
}



Scenario:
Assert following topic first:
..........................
1. testCodesInfo ==>>> Either some information is present or not [ its simple: And match response.testCodesInfo == "#present" ]
2. Object Array size ==> There are two array is present below...[assert it] because i request post method for two test
3. testMasterInformation
4. testCode ==>should be 004556
5. statTestCode ==> is present or not
6. statLocalized ==> is true or false
7. statApplicableTest ==>
8. testMasterExists
// this is a post response body:
{
"testCodes": ["004515", "004556"],
"branchCode": "CT",
"branchVerified": false,
"regionCode": "ATLANTIC"
}
// this is response of test response and i need to assert above 1 -8
"testCodesInfo": [ ==>>> assert ==>>> object array size
{
"testMasterInformation": { //==>>> assert array
"testCode": "004556", //==>>> assert
"testName": "hCG,Beta Subunit,Qual,Serum",
"orderable": true,
"specialityCode": null,
"procedureClassCode": "RI",
"procedureClassDesc": "RI-RIA",
"testType": "Test",
"publishedCode": "Published",
"labSystemCode": "LCLS",
"labSpecificInfo": []
},
"statTestCode": { //==>>> assert
"testNumber": "004556",
"testName": "HCG, Qual (Pregnancy)",
"regionCode": "A",
"regionName": "Atlantic",
"active": true
},
"statLocalized": false, // ==>>> assert
"statApplicableTest": true, //==>>> assert
"testMasterExists": true //==>>> assert (information about test)
},
{
"testMasterInformation": {
"testCode": "004515",
"testName": "Estradiol",
"orderable": true,
"specialityCode": null,
"procedureClassCode": "RI",
"procedureClassDesc": "RI-RIA",
"testType": "Panel",
"publishedCode": "Published",
"labSystemCode": "LCLS",
"labSpecificInfo": []
},
"statTestCode": {
"testNumber": "004515",
"testName": "Estradiol",
"regionCode": "A",
"regionName": "Atlantic",
"active": true
},
"statLocalized": false,
"statApplicableTest": true,
"testMasterExists": true
}
]
}

Solution: labcorp.feature
Background:
* url baseUrl
Scenario: get all testCodesInfo and validate if response is an array with size
Given path 'getTest'
When method get
Then status 200
## Assertion 1
#### This test if testCodesInfo is present or not
And match response contains { testCodesInfo : '#array' }
## Assertion 2
###************This tests the size of the response************
* def size = function(o){ return o.size() }
* def length = size(response.testCodesInfo)
* assert length == 2
* assert length != 1
#************************** END ******************************
## Assertion 3
#****** This here tests the first testMasterInformation*******
* def testCodes = response.testCodesInfo[0]
* match testCodes contains { testMasterInformation : '#object' }
#### Assertion 4 - 8
* match testCodes.testMasterInformation.testCode == "004556"
* match testCodes contains { statTestCode : '#object', statLocalized: '#boolean' }
* match testCodes contains { statApplicableTest: '#boolean', testMasterExists: '#boolean' }
* match testCodes.testMasterInformation.testCode == testCodes.statTestCode.testNumber
* def testCodesSecond = response.testCodesInfo[1]
* assert testCodesSecond.testMasterInformation.testCode == testCodesSecond.statTestCode.testNumber
* assert testCodesSecond.testMasterInformation.testCode == "004515"
* assert testCodesSecond.testMasterInformation.testCode != "004512"
* match testCodesSecond contains { statTestCode : '#object', statLocalized: '#boolean' }
* match testCodesSecond contains { statApplicableTest: '#boolean', testMasterExists: '#boolean' }

karate.config

function fn() {
var env = karate.env; // get java system property 'karate.env'
karate.log('karate.env selected enviroment was:', env);
Karate.configure("ssl", true)
if (!env) {
env = 'dev'; // a custom 'intelligent' default
} ////env can be anything: dev, qa, staging
var config = { // base config JSON
env: env,
appId: 'manoj',
appSecret: 'adhi',
baseUrl: 'https://labcorp' + env + ' org'/',
};
if (env == 'dev') {
config.appId: 'devmanoj'
config.appSecret: 'mypass'
// over-ride only those that need to be
// config.baseUrlDev = 'https://wsa.labcorp.com/dev';
} else if (env == 'qa') {
config.appId: 'qamanoj'
config.appSecret: 'mypass'
// config.baseUrlQA = 'https://wsa.labcorp.com/qa';
}
karate.log("My host: "+ config.baseUrl);
//or
//karate.log("Prod URL being used: "+ config.baseUrl);
//karate.log("Dev URL being used: "+ config.baseUrlQA);
//karate.log("QA URL being used: "+ config.baseUrlDev);
// don't waste time waiting for a connection or if servers don't respond within 5 seconds
karate.configure('connectTimeout', 5000);
karate.configure('readTimeout', 5000);
return config;
}


Jenkins Setup

No comments:

Post a Comment