rspec-json_matchers 0.1.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +20 -0
  4. data/Appraisals +16 -0
  5. data/CHANGELOG.md +7 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +497 -0
  9. data/Rakefile +14 -0
  10. data/gemfiles/rspec_3_0.gemfile +7 -0
  11. data/gemfiles/rspec_3_1.gemfile +7 -0
  12. data/gemfiles/rspec_3_2.gemfile +7 -0
  13. data/gemfiles/rspec_3_3.gemfile +7 -0
  14. data/lib/rspec/json_matchers/comparers/abstract_comparer.rb +289 -0
  15. data/lib/rspec/json_matchers/comparers/comparison_result.rb +22 -0
  16. data/lib/rspec/json_matchers/comparers/exact_keys_comparer.rb +27 -0
  17. data/lib/rspec/json_matchers/comparers/include_keys_comparer.rb +27 -0
  18. data/lib/rspec/json_matchers/comparers.rb +13 -0
  19. data/lib/rspec/json_matchers/expectation.rb +78 -0
  20. data/lib/rspec/json_matchers/expectations/abstract.rb +36 -0
  21. data/lib/rspec/json_matchers/expectations/core.rb +103 -0
  22. data/lib/rspec/json_matchers/expectations/mixins/built_in.rb +177 -0
  23. data/lib/rspec/json_matchers/expectations/private.rb +181 -0
  24. data/lib/rspec/json_matchers/expectations.rb +14 -0
  25. data/lib/rspec/json_matchers/matchers/be_json_matcher.rb +92 -0
  26. data/lib/rspec/json_matchers/matchers/be_json_with_content_matcher.rb +21 -0
  27. data/lib/rspec/json_matchers/matchers/be_json_with_sizes_matcher.rb +21 -0
  28. data/lib/rspec/json_matchers/matchers/be_json_with_something_matcher.rb +174 -0
  29. data/lib/rspec/json_matchers/matchers.rb +12 -0
  30. data/lib/rspec/json_matchers/utils/collection_keys_extractor.rb +35 -0
  31. data/lib/rspec/json_matchers/utils/key_path/extraction_result.rb +22 -0
  32. data/lib/rspec/json_matchers/utils/key_path/extractor.rb +70 -0
  33. data/lib/rspec/json_matchers/utils/key_path/path.rb +104 -0
  34. data/lib/rspec/json_matchers/utils.rb +10 -0
  35. data/lib/rspec/json_matchers/version.rb +8 -0
  36. data/lib/rspec/json_matchers.rb +15 -0
  37. data/lib/rspec-json_matchers.rb +1 -0
  38. data/rspec-json_matchers.gemspec +47 -0
  39. metadata +245 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: addd43fea47a6a22fa215a491070570ff1fa9f76
4
+ data.tar.gz: 54e450f71ec0af5594bc660f73bb9a3918356a89
5
+ SHA512:
6
+ metadata.gz: b761496f1884102fb0a0e79b93d75bbbb935c176fbf1eb83942c823678791b021ffbd376c403f56185933740cae6dc7a8820628f47cb9a25330c1d04c8370595
7
+ data.tar.gz: 88a84df10ffe3bd711b92fa801147ff49abe9f35f4d9be9b6319f1cbc2991755b2150d9211d7899262c5839e6c48d31c3723b820c46e1de1f3383ad86c370f75
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /.idea/
11
+ /.rspec
12
+ gemfiles/*.gemfile.lock
data/.travis.yml ADDED
@@ -0,0 +1,20 @@
1
+ # Send builds to container-based infrastructure
2
+ # http://docs.travis-ci.com/user/workers/container-based-infrastructure/
3
+ sudo: false
4
+ language: ruby
5
+ cache:
6
+ - bundler
7
+ rvm:
8
+ - 2.0
9
+ - 2.1
10
+ - 2.2
11
+ - ruby-head
12
+ gemfile:
13
+ - gemfiles/rspec_3_0.gemfile
14
+ - gemfiles/rspec_3_1.gemfile
15
+ - gemfiles/rspec_3_2.gemfile
16
+ - gemfiles/rspec_3_3.gemfile
17
+ matrix:
18
+ fast_finish: true
19
+ allow_failures:
20
+ - rvm: ruby-head
data/Appraisals ADDED
@@ -0,0 +1,16 @@
1
+
2
+ appraise "rspec_3_0" do
3
+ gem "rspec", "3.0"
4
+ end
5
+
6
+ appraise "rspec_3_1" do
7
+ gem "rspec", "3.1"
8
+ end
9
+
10
+ appraise "rspec_3_2" do
11
+ gem "rspec", "3.2"
12
+ end
13
+
14
+ appraise "rspec_3_3" do
15
+ gem "rspec", "3.3"
16
+ end
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+
5
+ ## Unreleased
6
+ ### Added
7
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rspec-json_matchers.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 PikachuEXE
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,497 @@
1
+ # RSpec::JsonMatchers
2
+
3
+ A collection of RSpec matchers for testing JSON data.
4
+
5
+ [![Gem Version](http://img.shields.io/gem/v/rspec-json_matchers.svg?style=flat-square)](http://badge.fury.io/rb/rspec-json_matchers)
6
+ [![License](https://img.shields.io/github/license/PikachuEXE/rspec-json_matchers.svg?style=flat-square)](http://badge.fury.io/rb/rspec-json_matchers)
7
+
8
+ [![Build Status](http://img.shields.io/travis/PikachuEXE/rspec-json_matchers.svg?style=flat-square)](https://travis-ci.org/PikachuEXE/rspec-json_matchers)
9
+ [![Dependency Status](http://img.shields.io/gemnasium/PikachuEXE/rspec-json_matchers.svg?style=flat-square)](https://gemnasium.com/PikachuEXE/rspec-json_matchers)
10
+
11
+ [![Code Climate](http://img.shields.io/codeclimate/github/PikachuEXE/rspec-json_matchers.svg?style=flat-square)](https://codeclimate.com/github/PikachuEXE/rspec-json_matchers)
12
+ [![Coverage Status](http://img.shields.io/coveralls/PikachuEXE/rspec-json_matchers.svg?style=flat-square)](https://coveralls.io/r/PikachuEXE/rspec-json_matchers)
13
+ [![Inch CI](https://inch-ci.org/github/PikachuEXE/rspec-json_matchers.svg?branch=master)](https://inch-ci.org/github/PikachuEXE/rspec-json_matchers)
14
+
15
+ [![Gitter](https://img.shields.io/badge/gitter-join%20chat-1dce73.svg?style=flat-square)](https://gitter.im/PikachuEXE/rspec-json_matchers)
16
+
17
+ This gem provides a collection of RSpec matchers for testing JSON data.
18
+ It aims to make JSON testing flexible & easier, especially for testing multiple properties.
19
+ It does not and will not have anything related to JSON Schema.
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ ```ruby
26
+ # `require` can be set to `true` safely without too much side effect
27
+ # (except having additional modules & classes defined which could be wasting memory).
28
+ # But there is no point requiring it unless in test
29
+ # Also maybe add it inside a "group"
30
+ gem 'rspec-json_matchers', require: false
31
+ ```
32
+
33
+ And then execute:
34
+
35
+ ```bash
36
+ $ bundle
37
+ ```
38
+
39
+ Or install it yourself as:
40
+
41
+ ```bash
42
+ $ gem install rspec-json_matchers
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ To include the new matchers in your examples,
48
+ add the following code somewhere which will be loaded by `rails_helper`/`spec_helper`:
49
+ ```ruby
50
+ # Remember the `required: false` suggested earlier?
51
+ # Now is the time that it is actually "required"
52
+ require "rspec-json_matchers"
53
+
54
+ # This will include matcher methods globally for all spec
55
+ # You can choose to include it conditionally, but you should decide yourself
56
+ # Or just ignore this comment if you are just trying this out
57
+ RSpec.configure do |config|
58
+ config.include RSpec::JsonMatchers::Matchers
59
+ end
60
+ ```
61
+
62
+ ### Matcher `be_json`
63
+
64
+ This is the starting point of all new matchers.
65
+ It can be used alone to ensure that the input can be parsed by `JSON` without error.
66
+
67
+ ```ruby
68
+ specify { expect("{}").to be_json } # => pass
69
+ specify { expect("[]").to be_json } # => pass
70
+ specify { expect("").to be_json } # => fail
71
+ ```
72
+
73
+ ### Matcher `be_json.with_content`
74
+
75
+ This is perhaps the most flexible & powerful matcher in this gem.
76
+
77
+
78
+ #### Content equivalence matching
79
+
80
+ When passing in "simple data values" (that represents one of JSON data types),
81
+ it matches when they have equivalent values (using `==`).
82
+ ```ruby
83
+ specify { expect("{}").to be_json.with_content(Hash.new) } # => pass
84
+ specify { expect("[]").to be_json.with_content(Array.new) } # => pass
85
+
86
+ specify { expect("{}").to be_json.with_content(Hash.new) } # => fail
87
+ specify { expect("[]").to be_json.with_content(Array.new) } # => fail
88
+
89
+ # The following line would fail when trying parse the input as JSON
90
+ # So you can be sure the input is a valid JSON
91
+ specify { expect("").to be_json.with_content(Hash.new) } # => fail
92
+ ```
93
+
94
+ Since it's common to have multiple "properties" in an object,
95
+ the gem allows multiple key as well, instead of having to create multiple examples for all properties you want to test.
96
+ ```ruby
97
+ # Ruby object + `to_json` + Symbol keys is used for easier typing in the following examples,
98
+ # but the actual JSON string won't change.
99
+
100
+ # Matching object with single key with String keys in expected
101
+ specify { expect({a: 1}.to_json).to be_json.with_content({"a" => 1}) } # => pass
102
+ # Matching object with single key with Symbol keys in expected
103
+ # Symbol keys will be used in the remaining examples, String keys can also be used interchangeably
104
+ specify { expect({a: 1}.to_json).to be_json.with_content({a: 1}) } # => pass
105
+
106
+ # Obviously
107
+ specify { expect({a: 1}.to_json).to be_json.with_content({a: 2}) } # => fail
108
+
109
+ # The input can have more keys than expected without failing by default
110
+ specify { expect({a: 1, b: 2}.to_json).to be_json.with_content({a: 1}) } # => pass
111
+ # The actual cannot have less keys than expected or will fail the example all the time
112
+ specify { expect({a: 1}.to_json).to be_json.with_content({a: 1, b: 2}) } # => fail
113
+ ```
114
+
115
+ It's possible to make examples fail when the object represented by JSON string in `subject`
116
+ contains more keys than that in expectation using `with_exact_keys`.
117
+
118
+ ```ruby
119
+ # The spec can be set to fail when actual has more keys than expected
120
+ specify { expect({a: 1, b: 2}.to_json).to be_json.with_content({a: 1}).with_exact_keys } # => fail
121
+ ```
122
+
123
+ A "path" can also be specified for testing deeply nested data.
124
+
125
+ ```ruby
126
+ context "when input is an Hash (in Ruby)" do
127
+ subject do
128
+ {
129
+ a: {
130
+ b: {
131
+ c: 1,
132
+ },
133
+ },
134
+ }.to_json
135
+ end
136
+
137
+ it { should be_json.with_content({a: {b: {c: 1}}}) } # => pass
138
+
139
+ it { should be_json.with_content({b: {c: 1}}).at_path("a") } # => pass
140
+ it { should be_json.with_content({c: 1}).at_path("a.b") } # => pass
141
+ it { should be_json.with_content(1).at_path("a.b.c") } # => pass
142
+
143
+ # subject without data at path will cause the example to fail
144
+ it { should be_json.with_content(1).at_path("a.b.d") } # => fail
145
+ it { should be_json.with_content(1).at_path("a.b.c.d") } # => fail
146
+
147
+ # Invalid path will cause the gem to fail, `should` or `should_not`
148
+ # To avoid false positive when used with `should_not`
149
+ it { should be_json.with_content("whatever").at_path(".") } # => fail
150
+ it { should be_json.with_content("whatever").at_path(".a.") } # => fail
151
+ it { should be_json.with_content("whatever").at_path("a..c") } # => fail
152
+
153
+ it { should_not be_json.with_content("whatever").at_path(".") } # => fail
154
+ it { should_not be_json.with_content("whatever").at_path(".a.") } # => fail
155
+ it { should_not be_json.with_content("whatever").at_path("a..c") } # => fail
156
+
157
+ # Digits can be used as well in path
158
+ specify { expect({'1' => {'2' => 1}}.to_json).to be_json.with_content({'2' => 1}).at_path("1") }
159
+ specify { expect({'1' => {'2' => 1}}.to_json).to be_json.with_content(1).at_path("1.2") }
160
+ end
161
+
162
+ context "when input is an Array (in Ruby)" do
163
+ subject do
164
+ [
165
+ [
166
+ [
167
+ [1],
168
+ ],
169
+ ],
170
+ ].to_json
171
+ end
172
+
173
+ it { should be_json.with_content([[[1]]]) } # => pass
174
+
175
+ it { should be_json.with_content([[1]]).at_path("0") } # => pass
176
+ it { should be_json.with_content([1]).at_path("0.0") } # => pass
177
+ it { should be_json.with_content(1).at_path("0.0.0") } # => pass
178
+
179
+ # subject without data at path will cause the example to fail
180
+ it { should be_json.with_content(1).at_path("0.0.1") } # => fail
181
+ it { should be_json.with_content(1).at_path("0.0.0.0") } # => fail
182
+
183
+ # Invalid path will cause the gem to fail, `should` or `should_not`
184
+ # To avoid false positive when used with `should_not`
185
+ it { should be_json.with_content("whatever").at_path(".") } # => fail
186
+ it { should be_json.with_content("whatever").at_path(".0.") } # => fail
187
+ it { should be_json.with_content("whatever").at_path("0..0") } # => fail
188
+
189
+ it { should_not be_json.with_content("whatever").at_path(".") } # => fail
190
+ it { should_not be_json.with_content("whatever").at_path(".0.") } # => fail
191
+ it { should_not be_json.with_content("whatever").at_path("0..0") } # => fail
192
+
193
+ # The following pass for `should_not`
194
+ # Since the matcher would not know the `actual` should match the path, not the reverse one
195
+ it { should be_json.with_content("whatever").at_path("a") } # => fail
196
+
197
+ it { should_not be_json.with_content("whatever").at_path("a") } # => pass
198
+ end
199
+ ```
200
+
201
+
202
+ #### Special content matching
203
+
204
+ Besides objects representing JSON data types, there are other objects that can be passed in as special expectation.
205
+
206
+ ```ruby
207
+ # Pass when subject is a String & matches the Regex
208
+ context "when expected is a Regexp" do
209
+ specify { expect({url: "https://domain.com"}.to_json).to be_json.with_content(url: %r|^https://|) } # => pass
210
+ specify { expect({url: "http://domain.com"}.to_json).to be_json.with_content(url: %r|^https://|) } # => fail
211
+ specify { expect({url: 1}.to_json).to be_json.with_content(url: %r|^https://|) } # => fail
212
+ end
213
+
214
+ # Pass when subject is "covered" by the Range
215
+ context "when expected is a Range" do
216
+ specify { expect({age: 1}.to_json).to be_json.with_content(age: (1...10)) } # => pass
217
+ specify { expect({age: 10}.to_json).to be_json.with_content(age: (1...10)) } # => fail
218
+ specify { expect({age: '1'}.to_json).to be_json.with_content(age: (1...10)) } # => fail
219
+
220
+ # Supports whatever Range supports, using #cover?
221
+ specify { expect({age: '1'}.to_json).to be_json.with_content(age: ('1'...'10')) } # => fail
222
+ end
223
+
224
+ # Pass when callable returns true
225
+ context "when expected is a callable" do
226
+ class ExampleCallable
227
+ def self.call(v)
228
+ new.call(v)
229
+ end
230
+
231
+ def call(v)
232
+ true
233
+ end
234
+ end
235
+
236
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: proc { true }) } # => pass
237
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: lambda {|_| true }) } # => pass
238
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: -> (_) { true }) } # => pass
239
+
240
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: ExampleCallable) } # => pass
241
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: ExampleCallable.new) } # => pass
242
+
243
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: -> { true }) } # => error
244
+ specify { expect({a: "whatever"}.to_json).to be_json.with_content(a: -> (a, b) { true }) } # => error
245
+ end
246
+
247
+ # Pass when subject's class (in Ruby form) inherits / same as expected
248
+ context "when expected is a callable" do
249
+ specify { expect({a: 1}.to_json).to be_json.with_content(a: String) } # => fail
250
+ specify { expect({a: 1}.to_json).to be_json.with_content(a: Integer) } # => pass
251
+ specify { expect({a: 1}.to_json).to be_json.with_content(a: Numeric) } # => pass
252
+ end
253
+ ```
254
+
255
+
256
+ #### Custom/Complex Expectations
257
+
258
+ Passing in a `Range` like (e.g. `('a'..'c')`) is telling the example to pass
259
+ only when the actual value equals to any of the values represented by the Range `'a' / 'b' / 'c'`.
260
+
261
+ But there is no way to specify other "OR" expectations easily (e.g. `'a' / 'c'`)
262
+ since `Array` is already used for data structure expectation.
263
+ So the gem also provides a list of classes to represent these kinds of custom expectations to be used.
264
+
265
+ ##### Setup
266
+
267
+ First, it requires some setup.
268
+ You can put the following code in any sensible place like a specific spec file or `rails_helper`.
269
+ ```ruby
270
+ module Expectations
271
+ include RSpec::JsonMatchers::Expectations::Mixins::BuiltIn
272
+ end
273
+ ```
274
+
275
+ Alternatively, you can use `let` to define a module without name,
276
+ to avoid creating top-namespaced constant
277
+ ```ruby
278
+ let(:expectations) do
279
+ Module.new do
280
+ include RSpec::JsonMatchers::Expectations::Mixins::BuiltIn
281
+ end
282
+ end
283
+ ```
284
+
285
+ If you really want to save typing `expectations::` and are not afraid of constant name conflicts,
286
+ You can add the following somewhere.
287
+ Note that you must use both `before(:each)` & `stub_const` to make this work.
288
+ Please tell us if you have other methods to achieve the same effect.
289
+ ```ruby
290
+ before(:each) do
291
+ RSpec::JsonMatchers::Expectations::Mixins::BuiltIn.constants.each do |expectation_klass_name|
292
+ stub_const(
293
+ expectation_klass_name.to_s,
294
+ RSpec::JsonMatchers::Expectations::Mixins::BuiltIn.const_get(expectation_klass_name),
295
+ )
296
+ end
297
+ end
298
+ ```
299
+
300
+ ##### Usage
301
+
302
+ Now let's take a look at the actual expectation classes this gem provides:
303
+ ```ruby
304
+ # `Anything` is an expectation that always passes
305
+ # It has the same effect as passing `Object` in
306
+ # Since every Ruby form of JSON data type is an `Object`
307
+ # But this would be more verbose & clear
308
+ specify { expect({a: "a" }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
309
+ specify { expect({a: 1 }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
310
+ specify { expect({a: 1.1 }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
311
+ specify { expect({a: {} }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
312
+ specify { expect({a: [] }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
313
+ specify { expect({a: true }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
314
+ specify { expect({a: false}.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
315
+ specify { expect({a: nil }.to_json).to be_json.with_content(a: expectations::Anything) } # => pass
316
+
317
+
318
+ # `PositiveNumber` is an expectation that passes when subject is a `Numeric` and larger than 0
319
+ specify { expect({a: 1 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => pass
320
+ specify { expect({a: 1.1 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => pass
321
+ specify { expect({a: 0 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => fail
322
+ specify { expect({a: 0.0 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => fail
323
+ specify { expect({a: -1 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => fail
324
+ specify { expect({a: -1.1 }.to_json).to be_json.with_content(a: expectations::PositiveNumber) } # => fail
325
+
326
+
327
+ # `NegativeNumber` is an expectation that passes when subject is a `Numeric` and less than 0
328
+ specify { expect({a: 1 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => fail
329
+ specify { expect({a: 1.1 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => fail
330
+ specify { expect({a: 0 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => fail
331
+ specify { expect({a: 0.0 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => fail
332
+ specify { expect({a: -1 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => pass
333
+ specify { expect({a: -1.1 }.to_json).to be_json.with_content(a: expectations::NegativeNumber) } # => pass
334
+
335
+
336
+ # `BooleanValue` is an expectation that passes when subject is a `TrueClass` or `FalseClass`
337
+ specify { expect({a: "a" }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
338
+ specify { expect({a: 1 }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
339
+ specify { expect({a: 1.1 }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
340
+ specify { expect({a: {} }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
341
+ specify { expect({a: [] }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
342
+ specify { expect({a: true }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => pass
343
+ specify { expect({a: false}.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => pass
344
+ specify { expect({a: nil }.to_json).to be_json.with_content(a: expectations::BooleanValue) } # => fail
345
+
346
+
347
+ # `ArrayOf` is an expectation that passes when subject is an `Array` and
348
+ # **ALL** elements satisfy the expectation passed in
349
+ specify { expect({a: "a" }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
350
+ specify { expect({a: 1 }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
351
+ specify { expect({a: 1.1 }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
352
+ specify { expect({a: {} }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
353
+ specify { expect({a: [] }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => pass
354
+ specify { expect({a: true }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
355
+ specify { expect({a: false}.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
356
+ specify { expect({a: nil }.to_json).to be_json.with_content(a: expectations::ArrayOf[expectations::Anything]) } # => fail
357
+
358
+ # As you see it allows empty array by default
359
+ # Since {Enumerable#all?} returns `true` when collection is empty
360
+ # You can make it fail using optional argument in {#allow_empty} or {#disallow_empty}
361
+ # Notice that {#disallow_empty} has no optional argument to avoid reading as double negative
362
+ specify do
363
+ expect({a: []}.to_json).to be_json.
364
+ with_content(a: expectations::ArrayOf[expectations::Anything].allow_empty)
365
+ end # => pass
366
+ specify do
367
+ expect({a: []}.to_json).to be_json.
368
+ with_content(a: expectations::ArrayOf[expectations::Anything].allow_empty(true))
369
+ end # => pass
370
+ specify do
371
+ expect({a: []}.to_json).to be_json.
372
+ with_content(a: expectations::ArrayOf[expectations::Anything].allow_empty(false))
373
+ end # => fail
374
+ specify do
375
+ expect({a: []}.to_json).to be_json.
376
+ with_content(a: expectations::ArrayOf[expectations::Anything].disallow_empty)
377
+ end # => fail
378
+
379
+
380
+ # `ArrayWithSize` is an expectation that passes when subject is an `Array` and
381
+ # The size satisfies the `Fixnum`, `Bignum` or `Range` passed in
382
+ # Passing "expectation" with unexpected type would simply fail the example
383
+ # This also means using `should_not` with unexpected type of "expectation" always pass
384
+ specify do
385
+ expect({a: [1]}.to_json).to be_json.
386
+ with_content(a: expectations::ArrayWithSize[1])
387
+ end # => pass
388
+ specify do
389
+ expect({a: [1]}.to_json).to be_json.
390
+ with_content(a: expectations::ArrayWithSize[0..2])
391
+ end # => pass
392
+ specify do
393
+ expect({a: [1]}.to_json).to be_json.
394
+ with_content(a: expectations::ArrayWithSize[1.1])
395
+ end # => error
396
+
397
+ # You can pass more than 1 objects in as expectation
398
+ # It will pass when ANY of them "expects" the size
399
+ specify do
400
+ expect({a: [1]}.to_json).to be_json.
401
+ with_content(a: expectations::ArrayWithSize[0, 1, 3])
402
+ end # => pass
403
+
404
+
405
+ # `AnyOf` is an expectation that passes when **any** of "expectations" passed in
406
+ # "expects" the subject
407
+ # It will convert non `Expectation` objects into `Expectation` objects,
408
+ # just like using `with_content` alone
409
+ specify do
410
+ expect({a: 1}.to_json).to be_json.
411
+ with_content(a: expectations::AnyOf[1])
412
+ end # => pass
413
+ specify do
414
+ expect({a: 1}.to_json).to be_json.
415
+ with_content(a: expectations::AnyOf[0, 1, 2])
416
+ end # => pass
417
+ specify do
418
+ expect({a: 1}.to_json).to be_json.
419
+ with_content(a: expectations::AnyOf[false, expectations::Anything, false])
420
+ end # => pass
421
+ specify do
422
+ expect({a: 1}.to_json).to be_json.
423
+ with_content(a: expectations::AnyOf[false, false, false])
424
+ end # => fail
425
+
426
+
427
+ # `AllOf` is an expectation that passes when **all** of "expectations" passed in
428
+ # "expects" the subject
429
+ # It will convert non `Expectation` objects into `Expectation` objects,
430
+ # just like using `with_content` alone
431
+ specify do
432
+ expect({a: 1}.to_json).to be_json.
433
+ with_content(a: expectations::AllOf[1])
434
+ end # => pass
435
+ specify do
436
+ expect({a: 1}.to_json).to be_json.
437
+ with_content(a: expectations::AllOf[1, (1..2), expectations::PositiveNumber])
438
+ end # => pass
439
+ specify do
440
+ expect({a: 1}.to_json).to be_json.
441
+ with_content(a: expectations::AllOf[0, 1, 2])
442
+ end # => fail
443
+ specify do
444
+ expect({a: 1}.to_json).to be_json.
445
+ with_content(a: expectations::AllOf[false, expectations::Anything, false])
446
+ end # => fail
447
+ specify do
448
+ expect({a: 1}.to_json).to be_json.
449
+ with_content(a: expectations::AllOf[false, false, false])
450
+ end # => fail
451
+ ```
452
+
453
+
454
+ ### Matcher `be_json.with_sizes`
455
+
456
+ This is a more convenient version of using `be_json.with_sizes` only with `ArrayWithSize`.
457
+ Since you can just pass `Fixnum`, `Bignum` or `Range` without typing `ArrayWithSize`.
458
+ Things like `with_exact_keys` & `at_path` can still be used.
459
+
460
+ ```ruby
461
+ specify do
462
+ expect({a: [1]}.to_json).to be_json.
463
+ with_sizes(a: 1)
464
+ end # => pass
465
+ specify do
466
+ expect({a: [1]}.to_json).to be_json.
467
+ with_sizes(a: (0..2))
468
+ end # => pass
469
+ specify do
470
+ expect({a: [1]}.to_json).to be_json.
471
+ with_sizes(a: 1.1)
472
+ end # => error
473
+
474
+ # But you can no longer pass multiple "expectations" for `AnyOf` behaviour
475
+ specify do
476
+ expect({a: [1]}.to_json).to be_json.
477
+ with_content(a: [0, 1, 3])
478
+ end # => fail, expects {a: [[], [1], [1, 2, 3]])
479
+ ```
480
+
481
+
482
+ ### Matcher `be_json.with_types`
483
+
484
+ Unlike gems such as
485
+ [`airborne`](https://github.com/brooklynDev/airborne) or
486
+ [`json_spec`](https://github.com/collectiveidea/json_spec),
487
+ there is no such matcher.
488
+ Just use `be_json.with_content` with classes.
489
+
490
+
491
+ ## Contributing
492
+
493
+ 1. Fork it ( https://github.com/PikachuEXE/rspec-json_matchers/fork )
494
+ 2. Create your branch (Preferred to be prefixed with `feature`/`fix`/other sensible prefixes)
495
+ 3. Commit your changes (No version related changes will be accepted)
496
+ 4. Push to the branch on your forked repo
497
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require "appraisal"
2
+ require "rubygems"
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ if !ENV["APPRAISAL_INITIALIZED"] && !ENV["TRAVIS"]
9
+ task :default do
10
+ sh "appraisal install && rake appraisal spec"
11
+ end
12
+ else
13
+ task :default => :spec
14
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.1"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.2"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rspec", "3.3"
6
+
7
+ gemspec :path => "../"