api_matchers 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/History.markdown +5 -0
- data/LICENSE +22 -0
- data/README.markdown +129 -0
- data/Rakefile +2 -0
- data/api_matchers.gemspec +21 -0
- data/lib/api_matchers/core/find_in_json.rb +25 -0
- data/lib/api_matchers/core/rspec_matchers.rb +49 -0
- data/lib/api_matchers/core/setup.rb +58 -0
- data/lib/api_matchers/headers/base.rb +14 -0
- data/lib/api_matchers/headers/be_json.rb +17 -0
- data/lib/api_matchers/headers/be_xml.rb +17 -0
- data/lib/api_matchers/http_status_code/base.rb +32 -0
- data/lib/api_matchers/http_status_code/be_bad_request.rb +17 -0
- data/lib/api_matchers/http_status_code/be_internal_server_error.rb +17 -0
- data/lib/api_matchers/http_status_code/be_unauthorized.rb +17 -0
- data/lib/api_matchers/http_status_code/create_resource.rb +17 -0
- data/lib/api_matchers/response_body/base.rb +46 -0
- data/lib/api_matchers/response_body/have_json_node.rb +25 -0
- data/lib/api_matchers/response_body/have_node.rb +6 -0
- data/lib/api_matchers/response_body/have_xml_node.rb +19 -0
- data/lib/api_matchers/version.rb +3 -0
- data/lib/api_matchers.rb +45 -0
- data/spec/api_matchers/core/find_in_json_spec.rb +27 -0
- data/spec/api_matchers/core/setup_spec.rb +4 -0
- data/spec/api_matchers/headers/base_spec.rb +12 -0
- data/spec/api_matchers/headers/be_json_spec.rb +23 -0
- data/spec/api_matchers/headers/be_xml_spec.rb +27 -0
- data/spec/api_matchers/http_status_code/base_spec.rb +12 -0
- data/spec/api_matchers/http_status_code/be_bad_request_spec.rb +43 -0
- data/spec/api_matchers/http_status_code/be_internal_server_error_spec.rb +43 -0
- data/spec/api_matchers/http_status_code/be_unauthorized_spec.rb +43 -0
- data/spec/api_matchers/http_status_code/create_resource_spec.rb +43 -0
- data/spec/api_matchers/response_body/base_spec.rb +47 -0
- data/spec/api_matchers/response_body/have_json_node_spec.rb +101 -0
- data/spec/api_matchers/response_body/have_node_spec.rb +27 -0
- data/spec/api_matchers/response_body/have_xml_node_spec.rb +102 -0
- data/spec/spec_helper.rb +10 -0
- metadata +134 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color -f d
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.9.2@api_matchers --create
|
data/Gemfile
ADDED
data/History.markdown
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Tomas D'Stefano
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# API Matchers
|
2
|
+
|
3
|
+
Collection of RSpec matchers for create your API.
|
4
|
+
|
5
|
+
## Matchers
|
6
|
+
|
7
|
+
* be_in_xml
|
8
|
+
* be_a_json
|
9
|
+
* create_resource
|
10
|
+
* be_a_bad_request
|
11
|
+
* be_unauthorized
|
12
|
+
* be_internal_server_error
|
13
|
+
* have_node
|
14
|
+
* have_json_node
|
15
|
+
* have_xml_node
|
16
|
+
* be_json_eql
|
17
|
+
* be_xml_eql
|
18
|
+
|
19
|
+
## Install
|
20
|
+
|
21
|
+
gem install api_matchers
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Have Node Matcher
|
26
|
+
|
27
|
+
"{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:transaction)
|
28
|
+
|
29
|
+
"{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:id).with(54)
|
30
|
+
|
31
|
+
"{ 'error': '', 'transaction': { 'id': '55', 'status': 'waiting_payment' } }".should have_node(:error).with('not_authorized')
|
32
|
+
|
33
|
+
If you want to configure to make all **searches inside a root element**, you can do this:
|
34
|
+
|
35
|
+
APIMatchers.setup do |config|
|
36
|
+
config.root_element = :transaction
|
37
|
+
end
|
38
|
+
|
39
|
+
"{ 'transaction': { 'id': '54', 'status': 'paid' } }".should have_node(:id).with(54) # WILL PASS
|
40
|
+
|
41
|
+
"{ 'error': '', 'transaction': { 'id': '55', 'status': 'waiting_payment' } }".should have_node(:error).with('not_authorized') # WILL NOT PASS BECAUSE THE ERROR NODE ISN'T INSIDE THE TRANSACTION NODE
|
42
|
+
|
43
|
+
### HAVE NODE Matcher Configuration
|
44
|
+
|
45
|
+
You can configure if you want xml or json(**JSON is the default**):
|
46
|
+
|
47
|
+
APIMatchers.setup do |config|
|
48
|
+
config.content_type = :xml
|
49
|
+
end
|
50
|
+
|
51
|
+
'<transaction><id>200</id><status>paid</status></transaction>'.should have_node(:status).with('paid')
|
52
|
+
|
53
|
+
**Observation: You can use the *have_xml_node* or *have_json_node* if you don't want to configure everytime.**
|
54
|
+
|
55
|
+
You can configure the name of the method for example:
|
56
|
+
|
57
|
+
## Instead of this
|
58
|
+
response.body.should have_node(:foo)
|
59
|
+
|
60
|
+
## YOU can do this
|
61
|
+
APIMatchers.setup do |config|
|
62
|
+
config.body_method = :body
|
63
|
+
end
|
64
|
+
|
65
|
+
Then you can use without call the **#body** method:
|
66
|
+
|
67
|
+
response.should have_node(:foo).with('bar')
|
68
|
+
|
69
|
+
### Create Resource Matcher
|
70
|
+
|
71
|
+
This matchers see the HTTP STATUS CODE is equal to 201.
|
72
|
+
|
73
|
+
response.status.should create_resource
|
74
|
+
|
75
|
+
### BAD REQUEST Matcher
|
76
|
+
|
77
|
+
This BAD REQUEST is a matcher that see if the HTTP STATUS code is equal to 400.
|
78
|
+
|
79
|
+
response.status.should be_a_bad_request
|
80
|
+
response.status.should be_bad_request
|
81
|
+
|
82
|
+
### UNAUTHORIZED Matcher
|
83
|
+
|
84
|
+
This UNAUTHORIZED is a matcher that see if the HTTP STATUS code is equal to 401.
|
85
|
+
|
86
|
+
response.status.should be_unauthorized
|
87
|
+
response.body.should have_node(:message).with('Invalid Credentials')
|
88
|
+
|
89
|
+
### INTERNAL SERVER ERROR Matcher
|
90
|
+
|
91
|
+
This INTERNAL SERVER Error is a matcher that see if the HTTP STATUS code is equal to 500.
|
92
|
+
|
93
|
+
response.status.should be_internal_server_error
|
94
|
+
response.body.should have_node(:message).with('An Internal Error Occurs in our precious app. :S')
|
95
|
+
|
96
|
+
### HTTP STATUS CODE Configuration
|
97
|
+
|
98
|
+
You can configure the name method to call the http status code:
|
99
|
+
|
100
|
+
APIMatchers.setup do |config|
|
101
|
+
config.http_status_method = :status
|
102
|
+
end
|
103
|
+
|
104
|
+
Then you can use without call the **#status** method:
|
105
|
+
|
106
|
+
response.should create_resource
|
107
|
+
|
108
|
+
This configurations affects this matchers:
|
109
|
+
|
110
|
+
* create_resource
|
111
|
+
* be_a_bad_request
|
112
|
+
* be_internal_server_error
|
113
|
+
* be_unauthorized
|
114
|
+
|
115
|
+
### Be in XML Matcher
|
116
|
+
|
117
|
+
This is a matcher that see if the content type is xml:
|
118
|
+
|
119
|
+
response.content_type.should be_in_xml
|
120
|
+
|
121
|
+
### Be in JSON Matcher
|
122
|
+
|
123
|
+
This is a matcher that see if the content type is in JSON:
|
124
|
+
|
125
|
+
response.content_type.should be_in_json
|
126
|
+
|
127
|
+
### Acknowlegments
|
128
|
+
|
129
|
+
* Special thanks to Daniel Konishi to contribute in the product that I extracted the matchers to this gem.
|
data/Rakefile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/api_matchers/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Tomas D'Stefano"]
|
6
|
+
gem.email = ["tomas_stefano@successoft.com"]
|
7
|
+
gem.description = %q{Collection of RSpec matchers for create your API.}
|
8
|
+
gem.summary = %q{Collection of RSpec matchers for create your API.}
|
9
|
+
gem.homepage = "https://github.com/tomas-stefano/api_matchers"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "api_matchers"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = APIMatchers::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency 'rspec', '>= 2.11.0'
|
19
|
+
gem.add_dependency 'activesupport', '>= 3.2.6'
|
20
|
+
gem.add_dependency 'nokogiri', '>= 1.5.5'
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module Core
|
3
|
+
class FindInJSON
|
4
|
+
attr_reader :json
|
5
|
+
|
6
|
+
def initialize(json)
|
7
|
+
@json = json
|
8
|
+
end
|
9
|
+
|
10
|
+
def find(options={})
|
11
|
+
expected_key = options.fetch(:node).to_s
|
12
|
+
|
13
|
+
@json.each do |key, value|
|
14
|
+
if key == expected_key
|
15
|
+
return value
|
16
|
+
end
|
17
|
+
if value.is_a? Hash
|
18
|
+
return FindInJSON.new(value).find(node: expected_key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
nil # Don't find anything!
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module RSpecMatchers
|
3
|
+
def be_bad_request
|
4
|
+
::APIMatchers::HTTPStatusCode::BeBadRequest.new(::APIMatchers::Core::Setup)
|
5
|
+
end
|
6
|
+
alias :be_a_bad_request :be_bad_request
|
7
|
+
|
8
|
+
def be_internal_server_error
|
9
|
+
::APIMatchers::HTTPStatusCode::BeInternalServerError.new(::APIMatchers::Core::Setup)
|
10
|
+
end
|
11
|
+
alias :be_an_internal_server_error :be_internal_server_error
|
12
|
+
|
13
|
+
def be_unauthorized
|
14
|
+
::APIMatchers::HTTPStatusCode::BeUnauthorized.new(::APIMatchers::Core::Setup)
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_resource
|
18
|
+
::APIMatchers::HTTPStatusCode::CreateResource.new(::APIMatchers::Core::Setup)
|
19
|
+
end
|
20
|
+
alias :created_resource :create_resource
|
21
|
+
|
22
|
+
def be_xml
|
23
|
+
::APIMatchers::Headers::BeXML.new(::APIMatchers::Core::Setup)
|
24
|
+
end
|
25
|
+
alias :be_in_xml :be_xml
|
26
|
+
|
27
|
+
def be_json
|
28
|
+
::APIMatchers::Headers::BeJSON.new(::APIMatchers::Core::Setup)
|
29
|
+
end
|
30
|
+
alias :be_in_json :be_json
|
31
|
+
alias :be_a_json :be_json
|
32
|
+
|
33
|
+
def have_json_node(expected_node)
|
34
|
+
::APIMatchers::ResponseBody::HaveJsonNode.new(expected_node: expected_node, setup: ::APIMatchers::Core::Setup)
|
35
|
+
end
|
36
|
+
|
37
|
+
def have_xml_node(expected_node)
|
38
|
+
::APIMatchers::ResponseBody::HaveXmlNode.new(expected_node: expected_node, setup: ::APIMatchers::Core::Setup)
|
39
|
+
end
|
40
|
+
|
41
|
+
def have_node(expected_node)
|
42
|
+
if ::APIMatchers::Core::Setup.have_node_matcher.equal?(:json)
|
43
|
+
have_json_node(expected_node)
|
44
|
+
else
|
45
|
+
have_xml_node(expected_node)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module Core
|
3
|
+
class Setup
|
4
|
+
# The http status method that will be called when you call http status matchers
|
5
|
+
#
|
6
|
+
# ==== Examples
|
7
|
+
#
|
8
|
+
# response.status.should create_resource
|
9
|
+
# response.status.should be_bad_request
|
10
|
+
# response.status.should be_unauthorized
|
11
|
+
#
|
12
|
+
# # Instead calling #status everytime, you can configure:
|
13
|
+
#
|
14
|
+
# APIMatchers.setup do |config|
|
15
|
+
# config.http_status_method = :status
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# Then:
|
19
|
+
#
|
20
|
+
# response.should create_resource
|
21
|
+
# response.should be_bad_request
|
22
|
+
# response.should be_unauthorized
|
23
|
+
#
|
24
|
+
cattr_accessor :http_status_method
|
25
|
+
|
26
|
+
# The response body method that will be called when you call the have_node matchers
|
27
|
+
#
|
28
|
+
# ==== Examples
|
29
|
+
#
|
30
|
+
# response.body.should have_node(:foo)
|
31
|
+
# response.body.should have_node(:bar)
|
32
|
+
# response.body.should have_node(:baz)
|
33
|
+
#
|
34
|
+
# # Instead calling #body everytime, you can configure:
|
35
|
+
#
|
36
|
+
# APIMatchers.setup do |config|
|
37
|
+
# config.http_status_method = :body
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# Then:
|
41
|
+
#
|
42
|
+
# response.should have_node(:foo)
|
43
|
+
# response.should have_node(:bar)
|
44
|
+
# response.should have_node(:baz)
|
45
|
+
#
|
46
|
+
cattr_accessor :response_body_method
|
47
|
+
|
48
|
+
# The default have node matcher that will be used.
|
49
|
+
# This have_node matcher is useful when you just work with one content type in your API.
|
50
|
+
# Change to :xml if you want that have_node works ONLY with XML.
|
51
|
+
# If you work with xml and json in the same API, I recommend that you check the
|
52
|
+
# have_json_node and have_xml_node matchers.
|
53
|
+
#
|
54
|
+
cattr_accessor :have_node_matcher
|
55
|
+
self.have_node_matcher = :json
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module Headers
|
3
|
+
class BeJSON < Base
|
4
|
+
def expected_content_type
|
5
|
+
'application/json; charset=utf-8'
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected a JSON response with '#{expected_content_type}'. Got: '#{@actual}'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected to not be a JSON response. Got: '#{expected_content_type}'.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module Headers
|
3
|
+
class BeXML < Base
|
4
|
+
def expected_content_type
|
5
|
+
'application/xml; charset=utf-8'
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected a XML response with '#{expected_content_type}'. Got: '#{@actual}'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected to not be a XML response. Got: '#{expected_content_type}'.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module HTTPStatusCode
|
3
|
+
class Base
|
4
|
+
attr_reader :setup
|
5
|
+
|
6
|
+
def initialize(setup)
|
7
|
+
@setup = setup
|
8
|
+
end
|
9
|
+
|
10
|
+
# Matches the actual with the expected http status code
|
11
|
+
#
|
12
|
+
def matches?(actual)
|
13
|
+
@http_status_code = find_http_status_code(actual)
|
14
|
+
@http_status_code.equal?(expected_status_code)
|
15
|
+
end
|
16
|
+
|
17
|
+
def expected_status_code
|
18
|
+
raise NotImplementedError, "not implemented on #{self}"
|
19
|
+
end
|
20
|
+
|
21
|
+
# If have some configuration about the method to call on actual that method will be called.
|
22
|
+
#
|
23
|
+
def find_http_status_code(actual)
|
24
|
+
if @setup.http_status_method.present?
|
25
|
+
actual.send(@setup.http_status_method)
|
26
|
+
else
|
27
|
+
actual
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module HTTPStatusCode
|
3
|
+
class BeBadRequest < Base
|
4
|
+
def expected_status_code
|
5
|
+
400
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected that '#{@http_status_code}' to be a Bad Request with the status '400'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected that '#{@http_status_code}' to NOT be a Bad Request with the status '400'.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module HTTPStatusCode
|
3
|
+
class BeInternalServerError < Base
|
4
|
+
def expected_status_code
|
5
|
+
500
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected that '#{@http_status_code}' to be Internal Server Error with the status '500'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected that '#{@http_status_code}' to NOT be Internal Server Error.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module HTTPStatusCode
|
3
|
+
class BeUnauthorized < Base
|
4
|
+
def expected_status_code
|
5
|
+
401
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected that '#{@http_status_code}' to be Unauthorized with the status '401'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected that '#{@http_status_code}' to NOT be Unauthorized.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module HTTPStatusCode
|
3
|
+
class CreateResource < Base
|
4
|
+
def expected_status_code
|
5
|
+
201
|
6
|
+
end
|
7
|
+
|
8
|
+
def failure_message_for_should
|
9
|
+
%Q{expected that '#{@http_status_code}' to be Created Resource with the status '201'.}
|
10
|
+
end
|
11
|
+
|
12
|
+
def failure_message_for_should_not
|
13
|
+
%Q{expected that '#{@http_status_code}' to NOT be Created Resource.}
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module APIMatchers
|
2
|
+
module ResponseBody
|
3
|
+
class Base
|
4
|
+
attr_reader :setup, :expected_node, :actual
|
5
|
+
attr_writer :actual
|
6
|
+
|
7
|
+
def initialize(options={})
|
8
|
+
@expected_node = options.fetch(:expected_node)
|
9
|
+
@setup = options.fetch(:setup)
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(actual)
|
13
|
+
raise NotImplementedError, "not implemented on #{self}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def with(expected_value)
|
17
|
+
@with_value = expected_value
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def response_body
|
22
|
+
if @setup.response_body_method.present?
|
23
|
+
@actual.send(@setup.response_body_method)
|
24
|
+
else
|
25
|
+
@actual
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_message_for_should
|
30
|
+
"expected to have node called: '#{@expected_node}'" << added_message << ". Got: '#{response_body}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
def failure_message_for_should_not
|
34
|
+
"expected to NOT have node called: '#{@expected_node}'" << added_message << ". Got: '#{response_body}'"
|
35
|
+
end
|
36
|
+
|
37
|
+
def added_message
|
38
|
+
if @with_value
|
39
|
+
" with value: '#{@with_value}'"
|
40
|
+
else
|
41
|
+
""
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'active_support/core_ext/hash'
|
3
|
+
|
4
|
+
module APIMatchers
|
5
|
+
module ResponseBody
|
6
|
+
class HaveJsonNode < Base
|
7
|
+
def matches?(actual)
|
8
|
+
@actual = actual
|
9
|
+
json = begin
|
10
|
+
JSON.parse(response_body)
|
11
|
+
rescue
|
12
|
+
{}
|
13
|
+
end
|
14
|
+
|
15
|
+
node = Core::FindInJSON.new(json).find(node: @expected_node.to_s)
|
16
|
+
|
17
|
+
if @with_value
|
18
|
+
node.to_s == @with_value.to_s
|
19
|
+
else
|
20
|
+
node.present?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module APIMatchers
|
4
|
+
module ResponseBody
|
5
|
+
class HaveXmlNode < Base
|
6
|
+
def matches?(actual)
|
7
|
+
@actual = actual
|
8
|
+
xml = Nokogiri::XML(response_body)
|
9
|
+
node = xml.xpath("//#{@expected_node}").text
|
10
|
+
|
11
|
+
if @with_value
|
12
|
+
node == @with_value.to_s
|
13
|
+
else
|
14
|
+
node.present?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/api_matchers.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "api_matchers/version"
|
2
|
+
require "active_support/core_ext/object"
|
3
|
+
require "active_support/core_ext/class"
|
4
|
+
|
5
|
+
module APIMatchers
|
6
|
+
autoload :RSpecMatchers, 'api_matchers/core/rspec_matchers'
|
7
|
+
|
8
|
+
# HTTP Status Code Matchers
|
9
|
+
#
|
10
|
+
module HTTPStatusCode
|
11
|
+
autoload :Base, 'api_matchers/http_status_code/base'
|
12
|
+
autoload :BeBadRequest, 'api_matchers/http_status_code/be_bad_request'
|
13
|
+
autoload :BeInternalServerError, 'api_matchers/http_status_code/be_internal_server_error'
|
14
|
+
autoload :BeUnauthorized, 'api_matchers/http_status_code/be_unauthorized'
|
15
|
+
autoload :CreateResource, 'api_matchers/http_status_code/create_resource'
|
16
|
+
end
|
17
|
+
|
18
|
+
# Content Type Matchers
|
19
|
+
#
|
20
|
+
module Headers
|
21
|
+
autoload :Base, 'api_matchers/headers/base'
|
22
|
+
autoload :BeXML, 'api_matchers/headers/be_xml'
|
23
|
+
autoload :BeJSON, 'api_matchers/headers/be_json'
|
24
|
+
end
|
25
|
+
|
26
|
+
# Response Body Matchers
|
27
|
+
#
|
28
|
+
module ResponseBody
|
29
|
+
autoload :Base, 'api_matchers/response_body/base'
|
30
|
+
autoload :HaveJsonNode, 'api_matchers/response_body/have_json_node'
|
31
|
+
autoload :HaveXmlNode, 'api_matchers/response_body/have_xml_node'
|
32
|
+
autoload :HaveNode, 'api_matchers/response_body/have_node'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Core
|
36
|
+
#
|
37
|
+
module Core
|
38
|
+
autoload :FindInJSON, 'api_matchers/core/find_in_json'
|
39
|
+
autoload :Setup, 'api_matchers/core/setup'
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.setup
|
43
|
+
yield(::APIMatchers::Core::Setup)
|
44
|
+
end
|
45
|
+
end
|