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:
$ bundleOr install it yourself as:
$ gem install client-apiImport 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' => 'ahamilton@apigee.com', '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
endCreate client-api object with custom variable
api = ClientApi::Api.newRSpec 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":"prashanth@mail.com","first_name":"Prashanth","last_name":"Sams"}})
expect(api.status).to eq(200)
end
it "PATCH request" do
api.patch('/api/users/2', {"data":{"email":"prashanth@mail.com","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)
endkey 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_keyfield 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
nullwithnilin 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)
endAdd 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":"prashanth@mail.com","first_name":"Prashanth","last_name":"Sams"}}, {'Content-Type' => 'application/json', 'Accept' => 'application/json'})
expect(api.status).to eq(200)
endFull 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)
endBasic Authentication
ClientApi.configure do |config|
...
config.basic_auth = {'Username' => 'ahamilton@apigee.com', 'Password' => 'myp@ssw0rd'}
endCustom Timeout in secs
ClientApi.configure do |config|
...
config.time_out = 10 # in secs
endOutput as json template
ClientApi.configure do |config|
...
config.json_output = {'Dirname' => './output', 'Filename' => 'sample'}
endLogs are optional in this library; you can do so through config in
spec_helper.rb. The param,StoreFilesCountwill 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
endSingle 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": "janet.weaver@reqres.in",
"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": "janet.weaver@reqres.in",
"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

