HTTP REST API client for testing application APIs based on the ruby’s RSpec framework that binds a complete api automation framework setup within itself
- Custom Header, URL, and Timeout support
- URL query string customization
- Datatype and key-pair value validation
- Single key-pair response validation
- Multi key-pair response validation
- JSON response schema validation
- JSON response content validation
- JSON response size validation
- JSON response is empty? validation
- JSON response has specific key? validation
- JSON response array-list sorting validation (descending, ascending)
- Response headers validation
- JSON template as body and schema
- Support to store JSON responses of each tests for the current run
- Logs support for debug
- Custom logs remover
- Auto-handle SSL for http(s) schemes
Add this line to your application's Gemfile:
gem 'client-api'
And then execute:
$ bundle
Or install it yourself as:
$ gem install client-api
Import the library in your env file
require 'client-api'
Add this config snippet in the spec_helper.rb
file:
ClientApi.configure do |config|
# all these configs are optional; comment out the config if not required
config.base_url = 'https://reqres.in'
config.headers = {'Content-Type' => 'application/json', 'Accept' => 'application/json'}
config.basic_auth = {'Username' => '[email protected]', 'Password' => 'myp@ssw0rd'}
config.json_output = {'Dirname' => './output', 'Filename' => 'test'}
config.time_out = 10 # in secs
config.logger = {'Dirname' => './logs', 'Filename' => 'test', 'StoreFilesCount' => 2}
# add this snippet only if the logger is enabled
config.before(:each) do |scenario|
ClientApi::Request.new(scenario)
end
end
Create client-api
object with custom variable
api = ClientApi::Api.new
RSpec test scenarios look like,
it "GET request" do
api = ClientApi::Api.new
api.get('/api/users')
expect(api.status).to eq(200)
expect(api.code).to eq(200)
expect(api.message).to eq('OK')
end
it "POST request" do
api.post('/api/users', {"name": "prashanth sams"})
expect(api.status).to eq(201)
end
it "DELETE request" do
api.delete('/api/users/3')
expect(api.status).to eq(204)
end
it "PUT request" do
api.put('/api/users/2', {"data":{"email":"[email protected]","first_name":"Prashanth","last_name":"Sams"}})
expect(api.status).to eq(200)
end
it "PATCH request" do
api.patch('/api/users/2', {"data":{"email":"[email protected]","first_name":"Prashanth","last_name":"Sams"}})
expect(api.status).to eq(200)
end
# For exceptional cases with body in the GET/DELETE request
it "GET request with JSON body" do
api.get_with_body('/api/users', { "count": 2 })
expect(api.status).to eq(200)
end
it "DELETE request with JSON body" do
api.delete_with_body('/api/users', { "count": 2 })
expect(api.status).to eq(200)
end
# Customize URL query string as a filter
it "Custom URL query string" do
api.get(
{
:url => '/location?',
:query => {
'sort': 'name',
'fields[count]': '50',
'fields[path_prefix]': '6',
'filter[name]': 'Los Angels'
}
}
)
end
# For POST request with multi-form as body
it "POST request with multi-form as body" do
api.post('/api/upload',
payload(
'type' => 'multipart/form-data',
'data' => {
'file': './data/request/upload.png'
}
)
)
expect(api.code).to eq(200)
end
key features
- datatype validation
- key-pair value validation
- value size validation
- is value empty validation
- key exist or key not-exist validation
- single key-pair validation
- multi key-pair validation
what to know?
- operator field is optional when
"operator": "=="
- exception is handled for the invalid key (say,
key: 'post->0->name'
), if thehas_key
field is not added in the validation
General Syntax | Syntax | Model 2 | Syntax | Model 3 | Syntax | Model 4 | Syntax | Model 5 | Syntax | Model 6 |
---|---|---|---|---|---|
validate( api.body, { key: '', operator: '', value: '', type: '' } ) |
validate( api.body, { key: '', size: 0 } ) |
validate( api.body, { key: '', empty: true } ) |
validate( api.body, { key: '', has_key: true } ) |
validate( api.body, { key: '', operator: '', value: '', type: '', size: 2, empty: true, has_key: false } ) |
validate( api.body, { key: '', type: '' }, { key: '', operator: '', value: '' } ) |
key benefits
- the most recommended validation for fixed / static JSON responses
- validates each JSON content value
what to know?
- replace
null
withnil
in the expected json (whenever applicable); cos, ruby don't know what isnull
General Syntax | Syntax | Model 2 |
---|---|
validate_json( { "data": { "id": 2, "first_name": "Prashanth", "last_name": "Sams", } }, { "data": { "id": 2, "first_name": "Prashanth", "last_name": "Sams", } } ) |
validate_json( api.body, { "data": { "id": 2, "first_name": "Prashanth", "last_name": "Sams", "link": nil } } ) |
key benefits
- validates an array of response key-pair values with ascending or descending soring algorithm. For more details, check
sort_spec.rb
General Syntax | Syntax | Model 2 |
---|---|
validate_list( api.body, { "key": "posts", "unit": "id", "sort": "ascending" } ) |
validate_list( api.body, { "key": "posts", "unit": "id", "sort": "descending" } ) |
key benefits
- validates any response headers
General Syntax | Syntax | Model 2 |
---|---|
validate_headers( api.response_headers, { key: '', operator: '', value: '' } ) |
validate_headers( api.response_headers, { key: "connection", operator: "!=", value: "open" },{ key: "vary", operator: "==", value: "Origin, Accept-Encoding" } ) |
Using json
template as body
it "JSON template as body" do
api.post('/api/users', payload("./data/request/post.json"))
expect(api.status).to eq(201)
end
Add custom header
it "GET request with custom header" do
api.get('/api/users', {'Content-Type' => 'application/json', 'Accept' => 'application/json'})
expect(api.status).to eq(200)
end
it "PATCH request with custom header" do
api.patch('/api/users/2', {"data":{"email":"[email protected]","first_name":"Prashanth","last_name":"Sams"}}, {'Content-Type' => 'application/json', 'Accept' => 'application/json'})
expect(api.status).to eq(200)
end
Full url support
it "full url", :post do
api.post('https://api.enterprise.apigee.com/v1/organizations/ahamilton-eval',{},{'Authorization' => 'Basic YWhhbWlsdG9uQGFwaWdlZS5jb206bXlwYXNzdzByZAo'})
expect(api.status).to eq(403)
end
Basic Authentication
ClientApi.configure do |config|
...
config.basic_auth = {'Username' => '[email protected]', 'Password' => 'myp@ssw0rd'}
end
Custom Timeout in secs
ClientApi.configure do |config|
...
config.time_out = 10 # in secs
end
Output as json
template
ClientApi.configure do |config|
...
config.json_output = {'Dirname' => './output', 'Filename' => 'sample'}
end
Logs are optional in this library; you can do so through config in
spec_helper.rb
. The param,StoreFilesCount
will keep the custom files as logs; you can remove it, if not needed.
ClientApi.configure do |config|
...
config.logger = {'Dirname' => './logs', 'Filename' => 'test', 'StoreFilesCount' => 5}
config.before(:each) do |scenario|
ClientApi::Request.new(scenario)
end
end
Single key-pair value JSON response validator
Validates JSON response value
, datatype
, size
, is value empty?
, and key exist?
validate(
api.body,
{
"key": "name",
"value": "prashanth sams",
"operator": "==",
"type": 'string'
}
)
Multi key-pair values response validator
Validates more than one key-pair values
validate(
api.body,
{
"key": "name",
"value": "prashanth sams",
"type": 'string'
},
{
"key": "event",
"operator": "eql?",
"type": 'boolean'
},
{
"key": "posts->1->enabled",
"value": false,
"operator": "!=",
"type": 'boolean'
},
{
"key": "profile->name->id",
"value": 2,
"operator": "==",
"type": 'integer'
},
{
"key": "profile->name->id",
"value": 2,
"operator": "<",
"type": 'integer'
},
{
"key": "profile->name->id",
"operator": ">=",
"value": 2,
},
{
"key": "post1->0->name",
"operator": "contains",
"value": "Sams"
},
{
"key": "post2->0->id",
"operator": "include",
"value": 34,
"type": 'integer'
},
{
"key": "post1->0->available",
"value": true,
"operator": "not contains",
"type": "boolean"
}
)
JSON response size validator
Validates the total size of the JSON array
validate(
api.body,
{
"key": "name",
"size": 2
},
{
"key": "name",
"operator": "==",
"value": "Sams",
"type": "string",
"has_key": true,
"empty": false,
"size": 2
}
)
JSON response value empty? validator
Validates if the key has empty value or not
validate(
api.body,
{
"key": "0->name",
"empty": false
},
{
"key": "name",
"operator": "==",
"value": "Sams",
"type": "string",
"size": 2,
"has_key": true,
"empty": false
}
)
JSON response has specific key? validator
Validates if the key exist or not
validate(
api.body,
{
"key": "0->name",
"has_key": true
},
{
"key": "name",
"operator": "==",
"value": "",
"type": "string",
"size": 2,
"empty": true,
"has_key": true
}
)
Type | options |
---|---|
Equal | = , == , eql , eql? , equal , equal? |
Not Equal | ! , != , !eql , !eql? , not eql , not equal , !equal? |
Greater than | > , >= , greater than , greater than or equal to |
Less than | < , <= , less than , less than or equal to , lesser than , lesser than or equal to |
Contains | contains , has , contains? , has? , include , include? |
Not Contains | not contains , !contains , not include , !include |
Type | options |
---|---|
String | string , str |
Integer | integer , int |
Symbol | symbol , sym |
Boolean | boolean , bool |
Array | array , arr |
Object | object , obj |
Float | float |
Hash | hash |
Complex | complex |
Rational | rational |
Fixnum | fixnum |
Falseclass | falseclass , false |
Trueclass | trueclass , true |
Bignum | bignum |
validate_schema(
schema_from_json('./data/schema/get_user_schema.json'),
{
"data":
{
"id": 2,
"email": "[email protected]",
"firstd_name": "Janet",
"last_name": "Weaver",
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
}
}
)
validate_schema(
{
"required": [
"data"
],
"type": "object",
"properties": {
"data": {
"type": "object",
"required": [
"id", "email", "first_name", "last_name", "avatar"
],
"properties": {
"id": {
"type": "integer"
},
"email": {
"type": "string"
},
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"avatar": {
"type": "string"
}
}
}
}
},
{
"data":
{
"id": 2,
"email": "[email protected]",
"first_name": "Janet",
"last_name": "Weaver",
"avatar": "https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"
}
}
)
validate_schema(
schema_from_json('./data/schema/get_user_schema.json'),
api.body
)
json response content value validation as a structure
actual_body = {
"posts":
{
"prashanth": {
"id": 1,
"title": "Post 1"
},
"sams": {
"id": 2,
"title": "Post 2"
}
},
"profile":
{
"id": 44,
"title": "Post 44"
}
}
validate_json( actual_body,
{
"posts":
{
"prashanth": {
"id": 1,
"title": "Post 1"
},
"sams": {
"id": 2
}
},
"profile":
{
"title": "Post 44"
}
})
validate_json( api.body,
{
"posts": [
{
"id": 2,
"title": "Post 2"
}
],
"profile": {
"name": "typicode"
}
}
)
validate_headers(
api.response_headers,
{
key: "connection",
operator: "!=",
value: "open"
},
{
key: "vary",
operator: "==",
value: "Origin, Accept-Encoding"
}
)
Yes, you can use this demo as an example, https://github.com/prashanth-sams/client-api
rake spec