json_response_matchers 1.1.0

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f9d75ea0b51b5fe4ecbb1c396dfd73788d3b5c931d3c048f1b452b4f31fbda80
4
+ data.tar.gz: f7db535ebccc51e66eb9d41e6f24aca40d76226f5accd29300f34c9cdf042c13
5
+ SHA512:
6
+ metadata.gz: c129e180cac8ea2780c9135d217e25fada4bfc7f8269900fc14ac2997a1a5fd9c0a92620ae256e633cf5b498b0a71d50f5667668cf934d26f9dcc6cc37c3ca6f
7
+ data.tar.gz: 282bd1cb707e91d05fa1a05c058d52ccef806d53ecac35a335bc96748c78f2ba909386203452120037de6e6e77c4bff9db0fb0f92085d6dff11fda5637981cf7
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/Appraisals ADDED
@@ -0,0 +1,25 @@
1
+ if RUBY_VERSION < '2.4'
2
+ appraise 'activesupport-4' do
3
+ gem 'activesupport', '4.0.6'
4
+ end
5
+ end
6
+
7
+ if RUBY_VERSION >= '2.3'
8
+ appraise 'activesupport-5' do
9
+ gem 'activesupport', '5.0.0'
10
+ end
11
+
12
+ appraise 'activesupport-5.2.2' do
13
+ gem 'activesupport', '5.2.2'
14
+ end
15
+ end
16
+
17
+ appraise 'rspec-3' do
18
+ gem 'rspec', '3.0.0'
19
+ gem 'activesupport', '4.0.6' if RUBY_VERSION < '2.3'
20
+ end
21
+
22
+ appraise 'rspec-3.8' do
23
+ gem 'rspec', '3.8.0'
24
+ gem 'activesupport', '4.0.6' if RUBY_VERSION < '2.3'
25
+ end
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in json_response_matchers.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019 Szijjártó Nagy Misu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ # JsonResponseMatchers
2
+
3
+ [rspec](https://relishapp.com/rspec) matchers for testing json responses in a [rails](https://rubyonrails.org/) application.
4
+
5
+ ## Dependencies
6
+
7
+ * **ruby** `'>= 2.0.0'`
8
+ * **rspec** `'>= 3.0'` <!-- see composable matchers -->
9
+ * **activesupport** `'>= 4.0.6'` <!-- see https://github.com/rails/rails/pull/10887 -->
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ group :test do
17
+ gem 'json_response_matchers'
18
+ end
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ ## Usage
26
+
27
+ 1. include the matchers in your rspec config
28
+
29
+ ```ruby
30
+ # spec/rails_helper.rb
31
+ RSpec.configure do |config|
32
+ # you can include it in a general way
33
+ config.include JsonResponseMatchers
34
+ # but it is recommended to include it only for the request
35
+ config.include JsonResponseMatchers, type: :request
36
+ # or the request and controller specs
37
+ config.include JsonResponseMatchers, type: /request|controller/
38
+ end
39
+ ```
40
+
41
+ 2. write more concise request specs
42
+
43
+ ```ruby
44
+ # in your request spec examples
45
+ # instead of
46
+ item = JSON.parse(response.body)['item']
47
+ expect(item['name']).to eq 'item-name'
48
+ # use the #have_json_content matcher
49
+ expect(response).to have_json_content('item-name').at_key :item, :name
50
+
51
+ # instead of
52
+ items = JSON.parse(repsonse.body)['items']
53
+ expect(items.map { |item| item['id'] }).to match_array [ 1, 2, 3, 4, 5 ]
54
+ # use the #have_json_values matcher
55
+ expect(response).to have_json_values(1, 2, 3, 4, 5).for(:id).at_key :items
56
+ ```
57
+
58
+ ### general rules
59
+
60
+ The target of the matchers can be a json parsable string or an object with such a string at the method `#body` (like the test response in rails request tests).
61
+
62
+ ```ruby
63
+ expect('{"item":{"id":1,"name":"item-name"}}').to have_json_content('item-name').at_key :item, :name
64
+ # or if `response.body` contains the above json
65
+ expect(response).to have_json_content('item-name').at_key :item, :name
66
+ ```
67
+
68
+ The `#at_key` method is optional and can receive one or more `string`, `symbol` or `integer` keys.
69
+
70
+ ```ruby
71
+ expect('false').to have_json_content(false)
72
+
73
+ json = '{"items":[{"id":1,"name":"item-1"},{"id":2,"name":"item-2"}]}'
74
+ expect(json).to have_json_values(1, 2).for(:id).at_key 'items'
75
+ expect(json).to have_json_values(1, 2).for(:id).at_key :items
76
+ expect(json).to have_json_content('item-2').at_key :items, 1, 'name'
77
+ ```
78
+
79
+ Both matchers are [composable](https://relishapp.com/rspec/rspec-expectations/docs/composing-matchers).
80
+
81
+ ```ruby
82
+ expect('{"item":{"id":1},"user":{"name":"user"}}')
83
+ .to have_json_content(1).at_key(:item, :id)
84
+ .and have_json_values('user').at_key(:user, :name)
85
+
86
+ expect('{"items":[{"id":1},{"id":2}],"page":1}')
87
+ .to have_json_values(1, 2).for(:id).at_key(:item)
88
+ .and have_json_content(1).at_key(:page)
89
+ ```
90
+
91
+ ### #have_json_content
92
+
93
+ Checks single values.
94
+
95
+ * If expected value is not a `hash`, it checks equality
96
+
97
+ ```ruby
98
+ # all pass
99
+ expect('{"string":"string"}').to have_json_content('string').at_key :string
100
+ expect('{"number":123}').to have_json_content(123).at_key :number
101
+ expect('{"boolean":false}').to have_json_content(false).at_key :boolean
102
+ expect('{"array":["a",2]}').to have_json_content([ 'a', 2 ]).at_key :array
103
+ expect('{"null":null}').to have_json_content(nil).at_key :null
104
+ ```
105
+
106
+ * If expected value is a `hash`
107
+ * and `#with_full_match` is specified, it checks equality
108
+ * otherwise it checks inclusion
109
+ * accepts symbol keys
110
+
111
+ ```ruby
112
+ expect('{"id":1,"name":"i1"}').to have_json_content('id'=>1).with_full_match # fails
113
+ expect('{"id":1,"name":"i1"}').to have_json_content('id'=>1) # passes
114
+ expect('{"id":1,"name":"i1"}').to have_json_content(name: 'i1', id: 1).with_full_match # passes
115
+ ```
116
+
117
+
118
+ ### #have_json_values
119
+
120
+ Checks arrays. The expected values are passed as a parameter list (`*args`) to the matcher.
121
+
122
+ * the metod `#for` is required and can take a `string` or `symbol` value
123
+
124
+ ```ruby
125
+ items = [
126
+ { id: 1, name: 'item-1' },
127
+ { id: 2, name: 'item-2' },
128
+ { id: 3, name: 'item-3' }
129
+ ]
130
+ expect(items.to_json).to have_json_values(1, 2, 3).for('id') # passes
131
+ expect(items.to_json).to have_json_values(1, 2, 3).for(:id) # passes
132
+ expect(items.to_json).to have_json_values(*items) # fails with ArgumentError
133
+ ```
134
+
135
+ * checks order only if `#in_strict_order` is specified
136
+
137
+ ```ruby
138
+ expect(items.to_json).to have_json_values(1, 3, 2).for(:id) # passes
139
+ expect(items.to_json).to have_json_values(1, 3, 2).for(:id).in_strict_order # fails
140
+ ```
141
+
142
+ ## Contributing
143
+
144
+ Bug reports and pull requests are welcome on GitHub at https://github.com/SzNagyMisu/json_response_matchers.
145
+
146
+ ## License
147
+
148
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,27 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ DEFAULT_RUBY_VERSIONS = [ '2.0.0', '2.3.7', '2.5.1', '2.6.0-preview2' ].freeze
5
+
6
+ desc "Test against multiple ruby, activerecord and rspec versions - " +
7
+ "set RUBY_VERSIONS to the version(s) to test with (default: #{DEFAULT_RUBY_VERSIONS.join(',').inspect})"
8
+ task :full_test do
9
+ versions = ENV['RUBY_VERSIONS'] ? ENV['RUBY_VERSIONS'].split(',') : DEFAULT_RUBY_VERSIONS
10
+ started_at = Time.now
11
+
12
+ versions.each do |version|
13
+ sh "./bin/test #{version}"
14
+ end
15
+
16
+ time_elapsed = (Time.now - started_at).to_i
17
+ hours = time_elapsed / 3600
18
+ minutes = time_elapsed / 60 % 60
19
+ seconds = time_elapsed % 60
20
+ puts "\nTest suit complete\n" +
21
+ " ruby versions: #{versions.join(', ')}\n" +
22
+ " run in #{hours}h #{minutes}m #{seconds}s\n"
23
+ end
24
+
25
+ RSpec::Core::RakeTask.new(:spec)
26
+
27
+ task :default => :spec
data/bin/test ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ if [ $1 ]; then
6
+ # see https://rvm.io/workflow/scripting
7
+ if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then
8
+ source "$HOME/.rvm/scripts/rvm"
9
+ elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then
10
+ source "/usr/local/rvm/scripts/rvm"
11
+ else
12
+ printf "ERROR: An RVM installation was not found."
13
+ fi
14
+ rvm use $1
15
+ fi
16
+
17
+ bundle update
18
+ bundle exec appraisal update
19
+ bundle exec appraisal rspec
20
+
21
+ echo 'Success!'
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "4.0.6"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,56 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ json_response_matchers (1.1.0)
5
+ activesupport (>= 4.0.6)
6
+ rspec (~> 3.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.0.6)
12
+ i18n (~> 0.6, >= 0.6.9)
13
+ minitest (~> 4.2)
14
+ multi_json (~> 1.3)
15
+ thread_safe (~> 0.1)
16
+ tzinfo (~> 0.3.37)
17
+ appraisal (2.2.0)
18
+ bundler
19
+ rake
20
+ thor (>= 0.14.0)
21
+ concurrent-ruby (1.1.4)
22
+ diff-lcs (1.3)
23
+ i18n (0.9.5)
24
+ concurrent-ruby (~> 1.0)
25
+ minitest (4.7.5)
26
+ multi_json (1.13.1)
27
+ rake (10.5.0)
28
+ rspec (3.8.0)
29
+ rspec-core (~> 3.8.0)
30
+ rspec-expectations (~> 3.8.0)
31
+ rspec-mocks (~> 3.8.0)
32
+ rspec-core (3.8.0)
33
+ rspec-support (~> 3.8.0)
34
+ rspec-expectations (3.8.2)
35
+ diff-lcs (>= 1.2.0, < 2.0)
36
+ rspec-support (~> 3.8.0)
37
+ rspec-mocks (3.8.0)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.8.0)
40
+ rspec-support (3.8.0)
41
+ thor (0.20.3)
42
+ thread_safe (0.3.6)
43
+ tzinfo (0.3.55)
44
+
45
+ PLATFORMS
46
+ ruby
47
+
48
+ DEPENDENCIES
49
+ activesupport (= 4.0.6)
50
+ appraisal (~> 2.2)
51
+ bundler (~> 1.16)
52
+ json_response_matchers!
53
+ rake (~> 10.0)
54
+
55
+ BUNDLED WITH
56
+ 1.16.6
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "5.2.2"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ json_response_matchers (1.1.0)
5
+ activesupport (>= 4.0.6)
6
+ rspec (~> 3.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.2.2)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ appraisal (2.2.0)
17
+ bundler
18
+ rake
19
+ thor (>= 0.14.0)
20
+ concurrent-ruby (1.1.4)
21
+ diff-lcs (1.3)
22
+ i18n (1.5.3)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.11.3)
25
+ rake (10.5.0)
26
+ rspec (3.8.0)
27
+ rspec-core (~> 3.8.0)
28
+ rspec-expectations (~> 3.8.0)
29
+ rspec-mocks (~> 3.8.0)
30
+ rspec-core (3.8.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-expectations (3.8.2)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-mocks (3.8.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.8.0)
38
+ rspec-support (3.8.0)
39
+ thor (0.20.3)
40
+ thread_safe (0.3.6)
41
+ tzinfo (1.2.5)
42
+ thread_safe (~> 0.1)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ activesupport (= 5.2.2)
49
+ appraisal (~> 2.2)
50
+ bundler (~> 1.16)
51
+ json_response_matchers!
52
+ rake (~> 10.0)
53
+
54
+ BUNDLED WITH
55
+ 1.16.6
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activesupport", "5.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ json_response_matchers (1.1.0)
5
+ activesupport (>= 4.0.6)
6
+ rspec (~> 3.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.0.0)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (~> 0.7)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ appraisal (2.2.0)
17
+ bundler
18
+ rake
19
+ thor (>= 0.14.0)
20
+ concurrent-ruby (1.1.4)
21
+ diff-lcs (1.3)
22
+ i18n (0.9.5)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.11.3)
25
+ rake (10.5.0)
26
+ rspec (3.8.0)
27
+ rspec-core (~> 3.8.0)
28
+ rspec-expectations (~> 3.8.0)
29
+ rspec-mocks (~> 3.8.0)
30
+ rspec-core (3.8.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-expectations (3.8.2)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-mocks (3.8.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.8.0)
38
+ rspec-support (3.8.0)
39
+ thor (0.20.3)
40
+ thread_safe (0.3.6)
41
+ tzinfo (1.2.5)
42
+ thread_safe (~> 0.1)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ activesupport (= 5.0.0)
49
+ appraisal (~> 2.2)
50
+ bundler (~> 1.16)
51
+ json_response_matchers!
52
+ rake (~> 10.0)
53
+
54
+ BUNDLED WITH
55
+ 1.16.6
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.8.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ json_response_matchers (1.1.0)
5
+ activesupport (>= 4.0.6)
6
+ rspec (~> 3.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.2.2)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ appraisal (2.2.0)
17
+ bundler
18
+ rake
19
+ thor (>= 0.14.0)
20
+ concurrent-ruby (1.1.4)
21
+ diff-lcs (1.3)
22
+ i18n (1.5.3)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.11.3)
25
+ rake (10.5.0)
26
+ rspec (3.8.0)
27
+ rspec-core (~> 3.8.0)
28
+ rspec-expectations (~> 3.8.0)
29
+ rspec-mocks (~> 3.8.0)
30
+ rspec-core (3.8.0)
31
+ rspec-support (~> 3.8.0)
32
+ rspec-expectations (3.8.2)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.8.0)
35
+ rspec-mocks (3.8.0)
36
+ diff-lcs (>= 1.2.0, < 2.0)
37
+ rspec-support (~> 3.8.0)
38
+ rspec-support (3.8.0)
39
+ thor (0.20.3)
40
+ thread_safe (0.3.6)
41
+ tzinfo (1.2.5)
42
+ thread_safe (~> 0.1)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ appraisal (~> 2.2)
49
+ bundler (~> 1.16)
50
+ json_response_matchers!
51
+ rake (~> 10.0)
52
+ rspec (= 3.8.0)
53
+
54
+ BUNDLED WITH
55
+ 1.16.6
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.0.0"
6
+
7
+ gemspec path: "../"
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ json_response_matchers (1.1.0)
5
+ activesupport (>= 4.0.6)
6
+ rspec (~> 3.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (5.2.2)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 0.7, < 2)
14
+ minitest (~> 5.1)
15
+ tzinfo (~> 1.1)
16
+ appraisal (2.2.0)
17
+ bundler
18
+ rake
19
+ thor (>= 0.14.0)
20
+ concurrent-ruby (1.1.4)
21
+ diff-lcs (1.3)
22
+ i18n (1.5.3)
23
+ concurrent-ruby (~> 1.0)
24
+ minitest (5.11.3)
25
+ rake (10.5.0)
26
+ rspec (3.0.0)
27
+ rspec-core (~> 3.0.0)
28
+ rspec-expectations (~> 3.0.0)
29
+ rspec-mocks (~> 3.0.0)
30
+ rspec-core (3.0.4)
31
+ rspec-support (~> 3.0.0)
32
+ rspec-expectations (3.0.4)
33
+ diff-lcs (>= 1.2.0, < 2.0)
34
+ rspec-support (~> 3.0.0)
35
+ rspec-mocks (3.0.4)
36
+ rspec-support (~> 3.0.0)
37
+ rspec-support (3.0.4)
38
+ thor (0.20.3)
39
+ thread_safe (0.3.6)
40
+ tzinfo (1.2.5)
41
+ thread_safe (~> 0.1)
42
+
43
+ PLATFORMS
44
+ ruby
45
+
46
+ DEPENDENCIES
47
+ appraisal (~> 2.2)
48
+ bundler (~> 1.16)
49
+ json_response_matchers!
50
+ rake (~> 10.0)
51
+ rspec (= 3.0.0)
52
+
53
+ BUNDLED WITH
54
+ 1.16.6
@@ -0,0 +1,39 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "json_response_matchers/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "json_response_matchers"
8
+ spec.version = JsonResponseMatchers::VERSION
9
+ spec.authors = ["Szijjártó Nagy Misu"]
10
+ spec.email = ["szijjartonagy.misu@gmail.com"]
11
+
12
+ spec.summary = 'rspec matchers to test http responses with json content in rails'
13
+ spec.description = 'Provides two matchers (#have_json_content and #have_json_values) to make testing the content of json responses easy.'
14
+ spec.homepage = 'https://github.com/SzNagyMisu/json_response_matchers'
15
+ spec.license = "MIT"
16
+
17
+ if spec.respond_to?(:metadata)
18
+ spec.metadata["homepage_uri"] = spec.homepage
19
+ spec.metadata["source_code_uri"] = spec.homepage
20
+ else
21
+ raise "RubyGems 2.0 or newer is required to protect against " \
22
+ "public gem pushes."
23
+ end
24
+
25
+ spec.files = `git ls-files -- lib/*`.split("\n") + %w[ README.md LICENSE.txt json_response_matchers.gemspec ]
26
+ spec.bindir = "bin"
27
+ spec.test_files = `git ls-files -- gemfiles/* spec/*`.split("\n") + %w[ Appraisals Gemfile Rakefile .rspec bin/test ]
28
+ spec.executables = []
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.required_ruby_version = ">= 2.0.0"
32
+
33
+ spec.add_dependency "rspec", "~> 3.0"
34
+ spec.add_dependency "activesupport", ">= 4.0.6"
35
+
36
+ spec.add_development_dependency "bundler", "~> 1.16"
37
+ spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "appraisal", "~> 2.2"
39
+ end
@@ -0,0 +1,37 @@
1
+ require "json"
2
+ require 'active_support/core_ext/hash/keys'
3
+ require 'rspec'
4
+
5
+ module JsonResponseMatchers
6
+ class Base
7
+ attr_reader :expected, :actual
8
+
9
+ include RSpec::Matchers::Composable
10
+
11
+ def initialize expected
12
+ @expected = expected
13
+ end
14
+
15
+ def at_key *hash_keys
16
+ @keys = hash_keys
17
+ self
18
+ end
19
+
20
+ def diffable?
21
+ true
22
+ end
23
+
24
+
25
+ private
26
+
27
+ def extract_parsed_json_from json_or_response
28
+ json = json_or_response.respond_to?(:body) ? json_or_response.body : json_or_response
29
+ JSON.parse json
30
+ end
31
+
32
+ def fetch_from hash
33
+ ( @keys || [] ).each { |key| hash = hash.fetch(Integer === key ? key : key.to_s) }
34
+ hash
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,36 @@
1
+ require "json_response_matchers/base"
2
+
3
+ module JsonResponseMatchers
4
+ class Content < Base
5
+ def initialize expected
6
+ super
7
+ @exact = true
8
+ if Hash === @expected
9
+ @expected.deep_stringify_keys!
10
+ @exact = false
11
+ end
12
+ end
13
+
14
+ def failure_message
15
+ "expected\n #{@actual}\nto #{@exact ? 'equal' : 'include'}\n #{@expected}"
16
+ end
17
+
18
+
19
+ def matches? actual
20
+ @actual = fetch_from extract_parsed_json_from actual
21
+ @exact ? @actual == @expected : hash_include?
22
+ end
23
+
24
+ def with_full_match
25
+ @exact = true
26
+ self
27
+ end
28
+
29
+
30
+ private
31
+
32
+ def hash_include?
33
+ @expected.all? { |key, expected_value| @actual[key.to_s] == expected_value }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,49 @@
1
+ require "json_response_matchers/base"
2
+
3
+ module JsonResponseMatchers
4
+ class Values < Base
5
+
6
+ def initialize expected
7
+ super
8
+ @check_order = false
9
+ end
10
+
11
+ def failure_message
12
+ "expected\n #{@actual}\nto #{@check_order ? 'equal' : 'match'}\n #{@expected}"
13
+ end
14
+
15
+
16
+ def matches? actual
17
+ raise ArgumentError, "You must set one or more attributes for mapping with #for:\n have_json_values(#{@expected}).for(attribute_name(s))" unless @for
18
+ @actual = fetch_from extract_parsed_json_from actual
19
+ @actual = @actual.map do |item|
20
+ @for.one? ? item[@for.first] : @for.map { |attribute| item[attribute] }
21
+ end
22
+ @check_order ? @actual == @expected : arrays_match?
23
+ end
24
+
25
+ def for *attributes
26
+ @for = attributes.map &:to_s
27
+ self
28
+ end
29
+
30
+ def in_strict_order
31
+ @check_order = true
32
+ self
33
+ end
34
+
35
+
36
+ private
37
+
38
+ def arrays_match?
39
+ expected = @expected.dup
40
+ all_matches = @actual.all? do |actual_value|
41
+ if expected.include? actual_value
42
+ expected.delete actual_value
43
+ true
44
+ end
45
+ end
46
+ all_matches && expected.empty?
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ module JsonResponseMatchers
2
+ VERSION = "1.1.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ require "json_response_matchers/version"
2
+ require "json_response_matchers/content"
3
+ require "json_response_matchers/values"
4
+
5
+ module JsonResponseMatchers
6
+ def have_json_values *values
7
+ Values.new values
8
+ end
9
+
10
+ def have_json_content parsed_content
11
+ Content.new parsed_content
12
+ end
13
+ end
@@ -0,0 +1,99 @@
1
+ RSpec.describe '#have_json_content' do
2
+ let(:item) { { item: { name: 'item 1', count: 2 } } }
3
+ let(:json) { item.to_json }
4
+ let(:nested) { { items: [ item ] }.to_json }
5
+ let(:response) { Struct.new(:body).new json }
6
+
7
+ describe 'the expected value' do
8
+ it 'can be a string.' do
9
+ expect({ string: 'string' }.to_json).to have_json_content('string').at_key(:string)
10
+ end
11
+
12
+ it 'can be a number.' do
13
+ expect({ number: 123 }.to_json).to have_json_content(123).at_key(:number)
14
+ end
15
+
16
+ it 'can be a boolean.' do
17
+ expect({ boolean: false }.to_json).to have_json_content(false).at_key(:boolean)
18
+ end
19
+
20
+ it 'can be an array.' do
21
+ expect({ array: [ :a, 2 ] }.to_json).to have_json_content([ 'a', 2 ]).at_key(:array)
22
+ end
23
+
24
+ it 'can be nil.' do
25
+ expect({ null: nil }.to_json).to have_json_content(nil).at_key(:null)
26
+ end
27
+
28
+ describe 'can be a hash' do
29
+ it 'with string keys.' do
30
+ expect(json).to have_json_content('name' => 'item 1', 'count' => 2).at_key('item')
31
+ end
32
+
33
+ it 'with symbol keys.'do
34
+ expect(json).to have_json_content(name: 'item 1', count: 2).at_key(:item)
35
+ end
36
+
37
+ it 'with symbol keys in deep nesting.' do
38
+ expect(nested).to have_json_content(items: [ item: { name: 'item 1', count: 2 } ])
39
+ end
40
+ end
41
+ end
42
+
43
+
44
+ it 'works for the response object.' do
45
+ expect(response).to have_json_content(name: 'item 1', count: 2).at_key(:item)
46
+ end
47
+
48
+ it 'works for the response body (json string).' do
49
+ expect(response.body).to have_json_content(name: 'item 1', count: 2).at_key(:item)
50
+ end
51
+
52
+ it 'works with no key passed.' do
53
+ expect(item[:item].to_json).to have_json_content(name: 'item 1', count: 2)
54
+ end
55
+
56
+ it 'works with multiple keys passed.' do
57
+ expect({ container: item }.to_json).to have_json_content(name: 'item 1', count: 2).at_key(:container, :item)
58
+ end
59
+
60
+ it 'works with symbol, string or integer key.' do
61
+ expect({ items: [ { name: 'item 1' } ] }.to_json).to have_json_content('item 1').at_key('items', 0, :name)
62
+ end
63
+
64
+ it 'checks inclusion by default.' do
65
+ expect(json).to have_json_content(name: 'item 1').at_key(:item)
66
+ expect(json).to have_json_content(count: 2).at_key(:item)
67
+ end
68
+
69
+ it 'checks equality if #with_full_match is set.' do
70
+ expect {
71
+ expect(json).to have_json_content(name: 'item 1').at_key(:item).with_full_match
72
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
73
+ expect(json).to have_json_content(count: 2, name: 'item 1').at_key(:item)
74
+ end
75
+
76
+ it 'fails if additional key value pairs are passed as expected.' do
77
+ expect {
78
+ expect(json).to have_json_content(name: 'item 1', count: 2, price: 100).at_key(:item)
79
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
80
+ expect {
81
+ expect(json).to have_json_content(name: 'item 1', count: 2, price: 100).at_key(:item).with_full_match
82
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
83
+ end
84
+
85
+ it 'fails with error message giving information about order check, actual, expected (and diff).' do
86
+ expect {
87
+ expect(json).to have_json_content(name: 'item 2').at_key(:item)
88
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError, /expected\n \{"name"=>"item 1", "count"=>2\}\nto include\n \{"name"=>"item 2"\}/
89
+ expect {
90
+ expect(json).to have_json_content(name: 'item 1').at_key(:item).with_full_match
91
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError, /expected\n \{"name"=>"item 1", "count"=>2\}\nto equal\n \{"name"=>"item 1"\}/
92
+ end
93
+
94
+ it 'works as compound matcher.' do
95
+ expect(json).to have_json_content(name: 'item 1', count: 2).at_key(:item).and include('"item":')
96
+ expect(json).to have_json_content(name: 'item 2', count: 1).at_key(:item).or include('"item":')
97
+ expect(response).to have_json_content(count: 2).at_key(:item).and have_json_content('item 1').at_key :item, :name
98
+ end
99
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.describe JsonResponseMatchers do
2
+ it "has a version number" do
3
+ expect(JsonResponseMatchers::VERSION).to eq '1.1.0'
4
+ end
5
+ end
@@ -0,0 +1,16 @@
1
+ require "bundler/setup"
2
+ require "json_response_matchers"
3
+
4
+ RSpec.configure do |config|
5
+ # Enable flags like --only-failures and --next-failure
6
+ config.example_status_persistence_file_path = ".rspec_status" if RSpec::Core::Version::STRING >= '3.3.0'
7
+
8
+ # Disable RSpec exposing methods globally on `Module` and `main`
9
+ config.disable_monkey_patching!
10
+
11
+ config.expect_with :rspec do |c|
12
+ c.syntax = :expect
13
+ end
14
+
15
+ config.include JsonResponseMatchers
16
+ end
@@ -0,0 +1,94 @@
1
+ RSpec.describe '#have_json_values' do
2
+ let(:items) do
3
+ {
4
+ items: [
5
+ { name: 'item 1', count: 2 },
6
+ { name: 'item 2', count: 11 },
7
+ { name: 'item 3', count: 0 }
8
+ ]
9
+ }
10
+ end
11
+ let(:json) { items.to_json }
12
+ let(:response) { Struct.new(:body).new items.to_json }
13
+
14
+ it 'works with string keys.' do
15
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3').for('name').at_key('items')
16
+ end
17
+
18
+ it 'works with symbol keys.' do
19
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:items)
20
+ end
21
+
22
+ it 'works with integer keys for array.' do
23
+ json_in_array = [ items ].to_json
24
+ expect(json_in_array).to have_json_values(2, 11, 0).for(:count).at_key 0, :items
25
+ end
26
+
27
+ it 'works for the response object.' do
28
+ expect(response).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:items)
29
+ end
30
+
31
+ it 'works for the response body (json string).' do
32
+ expect(response.body).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:items)
33
+ end
34
+
35
+ it 'works with no key passed.' do
36
+ expect(items[:items].to_json).to have_json_values('item 1', 'item 2', 'item 3').for(:name)
37
+ end
38
+
39
+ it 'works with multiple keys passed.' do
40
+ expect({ container: items }.to_json).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:container, :items)
41
+ end
42
+
43
+ it 'fails if no :for passed' do
44
+ expect {
45
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3').at_key(:items)
46
+ }.to raise_exception ArgumentError, /set one or more attributes/
47
+ end
48
+
49
+ it 'works with multiple :fors passed' do
50
+ expect(json).to have_json_values([ 'item 1', 2 ], [ 'item 2', 11 ], [ 'item 3', 0 ]).for(:name, :count).at_key(:items)
51
+ end
52
+
53
+ it 'works with falsy values too.' do
54
+ json = { items: [ { value: nil }, { value: false }, { value: true } ] }.to_json
55
+ expect(json).to have_json_values(nil, false, true).for(:value).at_key :items
56
+ end
57
+
58
+ it 'does not check array order by default.' do
59
+ expect(json).to have_json_values('item 2', 'item 1', 'item 3').for(:name).at_key(:items)
60
+ end
61
+
62
+ it 'checks array order if #in_strict_order is set.' do
63
+ expect {
64
+ expect(json).to have_json_values('item 2', 'item 1', 'item 3').in_strict_order.for(:name).at_key(:items)
65
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
66
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:items).in_strict_order
67
+ end
68
+
69
+ it 'fails if additional elements are passed as expected.' do
70
+ expect {
71
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3', 'item 4').for(:name).at_key(:items)
72
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
73
+ expect {
74
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3', 'item 4').for(:name).at_key(:items).in_strict_order
75
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError
76
+ end
77
+
78
+ it 'fails with error message giving information about order check, actual, expected (and diff).' do
79
+ expect {
80
+ expect(json).to have_json_values('item 1').for(:name).at_key(:items)
81
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError, /expected\n \["item 1", "item 2", "item 3"\]\nto match\n \["item 1"\]/
82
+ expect {
83
+ expect(json).to have_json_values('item 1').for(:name).at_key(:items).in_strict_order
84
+ }.to raise_exception RSpec::Expectations::ExpectationNotMetError, /expected\n \["item 1", "item 2", "item 3"\]\nto equal\n \["item 1"\]/
85
+ end
86
+
87
+ it 'works as compound matcher.' do
88
+ expect(json).to have_json_values('item 1', 'item 2', 'item 3').for(:name).at_key(:items).and include('"items":')
89
+ expect(json).to have_json_values('item 4', 'item 5', 'item 6').for(:name).at_key(:items).or include('"items":')
90
+ expect('{"items":[{"id":1},{"id":2}],"page":2}')
91
+ .to have_json_values(1, 2).for(:id).at_key(:items)
92
+ .and have_json_content(2).at_key(:page)
93
+ end
94
+ end
metadata ADDED
@@ -0,0 +1,163 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: json_response_matchers
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Szijjártó Nagy Misu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.6
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 4.0.6
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: appraisal
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.2'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.2'
83
+ description: 'Provides two matchers (#have_json_content and #have_json_values) to
84
+ make testing the content of json responses easy.'
85
+ email:
86
+ - szijjartonagy.misu@gmail.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".rspec"
92
+ - Appraisals
93
+ - Gemfile
94
+ - LICENSE.txt
95
+ - README.md
96
+ - Rakefile
97
+ - bin/test
98
+ - gemfiles/activesupport_4.gemfile
99
+ - gemfiles/activesupport_4.gemfile.lock
100
+ - gemfiles/activesupport_5.2.2.gemfile
101
+ - gemfiles/activesupport_5.2.2.gemfile.lock
102
+ - gemfiles/activesupport_5.gemfile
103
+ - gemfiles/activesupport_5.gemfile.lock
104
+ - gemfiles/rspec_3.8.gemfile
105
+ - gemfiles/rspec_3.8.gemfile.lock
106
+ - gemfiles/rspec_3.gemfile
107
+ - gemfiles/rspec_3.gemfile.lock
108
+ - json_response_matchers.gemspec
109
+ - lib/json_response_matchers.rb
110
+ - lib/json_response_matchers/base.rb
111
+ - lib/json_response_matchers/content.rb
112
+ - lib/json_response_matchers/values.rb
113
+ - lib/json_response_matchers/version.rb
114
+ - spec/content_spec.rb
115
+ - spec/json_response_matchers_spec.rb
116
+ - spec/spec_helper.rb
117
+ - spec/values_spec.rb
118
+ homepage: https://github.com/SzNagyMisu/json_response_matchers
119
+ licenses:
120
+ - MIT
121
+ metadata:
122
+ homepage_uri: https://github.com/SzNagyMisu/json_response_matchers
123
+ source_code_uri: https://github.com/SzNagyMisu/json_response_matchers
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 2.0.0
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.7.6
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: rspec matchers to test http responses with json content in rails
144
+ test_files:
145
+ - gemfiles/activesupport_4.gemfile
146
+ - gemfiles/activesupport_4.gemfile.lock
147
+ - gemfiles/activesupport_5.2.2.gemfile
148
+ - gemfiles/activesupport_5.2.2.gemfile.lock
149
+ - gemfiles/activesupport_5.gemfile
150
+ - gemfiles/activesupport_5.gemfile.lock
151
+ - gemfiles/rspec_3.8.gemfile
152
+ - gemfiles/rspec_3.8.gemfile.lock
153
+ - gemfiles/rspec_3.gemfile
154
+ - gemfiles/rspec_3.gemfile.lock
155
+ - spec/content_spec.rb
156
+ - spec/json_response_matchers_spec.rb
157
+ - spec/spec_helper.rb
158
+ - spec/values_spec.rb
159
+ - Appraisals
160
+ - Gemfile
161
+ - Rakefile
162
+ - ".rspec"
163
+ - bin/test