api_matchers 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|