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


Keywords
api, api-automation, api-client, api-test, api-test-framework, api-testing-framework, automation, datatype, json-response, rest, rest-api, restful, restful-api, ruby, schema, testing
License
MIT
Install
gem install client-api -v 0.4.1

Documentation

ClientApi

Gem Version Build Status

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

Features

  • 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

Installation

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'

#Usage outline

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
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":"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)
end

Validation shortcuts

Default validation

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 the has_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: ''
    }
)
            

JSON response content validation

key benefits

  • the most recommended validation for fixed / static JSON responses
  • validates each JSON content value

what to know?

  • replace null with nil in the expected json (whenever applicable); cos, ruby don't know what is null
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
            }
    }
)
            

JSON response sorting validation

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"
  }
)
            

JSON response headers validation

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"
    }
)
            

#General usage

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":"prashanth@mail.com","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' => 'ahamilton@apigee.com', '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

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

#Validation | more info.

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
    }
)
Operator
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
Datatype
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

JSON response schema validation

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 validation

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"
      }
  }
)

Response headers validation

validate_headers(
  api.response_headers,
  {
    key: "connection",
    operator: "!=",
    value: "open"
  },
  {
    key: "vary",
    operator: "==",
    value: "Origin, Accept-Encoding"
  }
)

Is there a demo available for this gem?

Yes, you can use this demo as an example, https://github.com/prashanth-sams/client-api

rake spec