api_matchers 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +17 -0
  2. data/.rspec +1 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +4 -0
  5. data/History.markdown +5 -0
  6. data/LICENSE +22 -0
  7. data/README.markdown +129 -0
  8. data/Rakefile +2 -0
  9. data/api_matchers.gemspec +21 -0
  10. data/lib/api_matchers/core/find_in_json.rb +25 -0
  11. data/lib/api_matchers/core/rspec_matchers.rb +49 -0
  12. data/lib/api_matchers/core/setup.rb +58 -0
  13. data/lib/api_matchers/headers/base.rb +14 -0
  14. data/lib/api_matchers/headers/be_json.rb +17 -0
  15. data/lib/api_matchers/headers/be_xml.rb +17 -0
  16. data/lib/api_matchers/http_status_code/base.rb +32 -0
  17. data/lib/api_matchers/http_status_code/be_bad_request.rb +17 -0
  18. data/lib/api_matchers/http_status_code/be_internal_server_error.rb +17 -0
  19. data/lib/api_matchers/http_status_code/be_unauthorized.rb +17 -0
  20. data/lib/api_matchers/http_status_code/create_resource.rb +17 -0
  21. data/lib/api_matchers/response_body/base.rb +46 -0
  22. data/lib/api_matchers/response_body/have_json_node.rb +25 -0
  23. data/lib/api_matchers/response_body/have_node.rb +6 -0
  24. data/lib/api_matchers/response_body/have_xml_node.rb +19 -0
  25. data/lib/api_matchers/version.rb +3 -0
  26. data/lib/api_matchers.rb +45 -0
  27. data/spec/api_matchers/core/find_in_json_spec.rb +27 -0
  28. data/spec/api_matchers/core/setup_spec.rb +4 -0
  29. data/spec/api_matchers/headers/base_spec.rb +12 -0
  30. data/spec/api_matchers/headers/be_json_spec.rb +23 -0
  31. data/spec/api_matchers/headers/be_xml_spec.rb +27 -0
  32. data/spec/api_matchers/http_status_code/base_spec.rb +12 -0
  33. data/spec/api_matchers/http_status_code/be_bad_request_spec.rb +43 -0
  34. data/spec/api_matchers/http_status_code/be_internal_server_error_spec.rb +43 -0
  35. data/spec/api_matchers/http_status_code/be_unauthorized_spec.rb +43 -0
  36. data/spec/api_matchers/http_status_code/create_resource_spec.rb +43 -0
  37. data/spec/api_matchers/response_body/base_spec.rb +47 -0
  38. data/spec/api_matchers/response_body/have_json_node_spec.rb +101 -0
  39. data/spec/api_matchers/response_body/have_node_spec.rb +27 -0
  40. data/spec/api_matchers/response_body/have_xml_node_spec.rb +102 -0
  41. data/spec/spec_helper.rb +10 -0
  42. metadata +134 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in api_matchers.gemspec
4
+ gemspec
data/History.markdown ADDED
@@ -0,0 +1,5 @@
1
+ ## development
2
+
3
+ 1) Headers Matchers: be_xml, be_json (**OBS:** Need to think about the setup!)
4
+ 2) HTTP Status Matchers: be_a_bad_request, be_internal_server_error, be_unauthorized, create_resource
5
+ 3) Response body Matchers: have_node, have_json_node, have_xml_node
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,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -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,14 @@
1
+ module APIMatchers
2
+ module Headers
3
+ class Base
4
+ def matches?(actual)
5
+ @actual = actual
6
+ actual.eql?(expected_content_type)
7
+ end
8
+
9
+ def expected_content_type
10
+ raise NotImplementedError, "not implemented on #{self}"
11
+ end
12
+ end
13
+ end
14
+ 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,6 @@
1
+ module APIMatchers
2
+ module ResponseBody
3
+ class HaveNode
4
+ end
5
+ end
6
+ 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
@@ -0,0 +1,3 @@
1
+ module APIMatchers
2
+ VERSION = "0.0.1"
3
+ end
@@ -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