HTTP Rest Api client for RSpec test automation framework that binds within itself
- Custom Header, URL, and Timeout support
- Datatype and key-pair value validation
- Single key-pair response validation
- Multi key-pair response validation
- JSON response schema validation
- JSON response content validation
- 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)
endkey features
- datatype validation
- key-pair value validation
- single key-pair validation
- multi key-pair validation
| General Syntax | Syntax | Model 2 | Syntax | Model 3 |
|---|---|---|
validate(
api.body,
{
key: '',
value: '',
operator: '',
type: ''
}
)
|
validate(
api.body,
{
key: '',
value: '',
operator: '',
}
)
|
validate(
api.body,
{
key: '',
operator: '',
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 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
endValidate .json response
valuesanddatatype; validates single key-pair values in the response
validate(
api.body,
{
"key": "name",
"value": "prashanth sams",
"operator": "==",
"type": 'string'
}
)Multi key-pair values response validator
validate(
api.body,
{
"key": "name",
"value": "prashanth sams",
"operator": "==",
"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"
}
)| Type | options |
|---|---|
| Equal | =, ==, eql?, equal, equal? |
| Not Equal | !, !=, !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


