rspec-json_api_matchers 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80f25d71ac3d429010809356ae1420279e37656b
4
+ data.tar.gz: 5b1b1d1fc5530fbf695339b4702ad2558d0cb865
5
+ SHA512:
6
+ metadata.gz: ae8da9b3966cc8979db0a36d31943ee5f70018d3cee2bd5255f28ec33b6e144ecf6b9f669c023cc5997f1881e5110aebbda8f389db78c402a190a0e60cd5e82f
7
+ data.tar.gz: a2c311a5c42937970e60935c0505cc44be039d5981cb4f4fb1f45abcfc66d1fd280da2b56a2bbf375a3762258ba2c523bd35dca58bb18a75267ed069b24c8e46
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rspec-json_api_matchers.gemspec
4
+ gemspec
@@ -0,0 +1,165 @@
1
+ # RSpec::JsonApiMatchers
2
+
3
+ This gem provides a set of matchers that make testing JSON documents (actually
4
+ the hashes parsed from them) simpler and more elegant.
5
+
6
+ It provides two matchers, `have_attribute` and `match_json`.
7
+
8
+ ## `#have_attribute`
9
+
10
+ Use this matcher when you want to examine a single attribute, and optionally
11
+ match against its value.
12
+
13
+ ### Example Usage
14
+
15
+ ```ruby
16
+ RSpec.describe "my hash" do
17
+ include RSpec::JsonApiMatchers
18
+
19
+ subject(:response_document) do
20
+ {
21
+ author: "Paul",
22
+ gems_published: 42,
23
+ created_at: "2016-01-01T00:00:00Z"
24
+ }
25
+ end
26
+
27
+ # Test that the key is present, regardless of value (even nil)
28
+ it { should have_attribute :author }
29
+
30
+ # Test the value by using another matcher
31
+ it { should have_attribute :author, eq("Paul") }
32
+ it { should have_attribute :author, match(/Paul/) }
33
+ it { should have_attribute :gems_published, be > 40 }
34
+ it { should have_attribute :created_at, iso8601_timestamp }
35
+ end
36
+ ```
37
+
38
+ It will also provide nice descriptions in the rspec doc format, and useful
39
+ failure messages:
40
+
41
+ ```
42
+ my hash
43
+ should have attribute :author be present
44
+ should have attribute :author eq "Paul"
45
+ should have attribute :author match /Paul/
46
+ should have attribute :gems_published be > 40
47
+ should have attribute :created_at match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
48
+ ```
49
+
50
+ ```
51
+ Failures:
52
+
53
+ 1) my hash should have attribute :full_name be present
54
+ Failure/Error: it { should have_attribute :full_name }
55
+ Expected attribute :full_name to be present
56
+ # ./spec/examples_spec.rb:24:in `block (2 levels) in <top (required)>'
57
+
58
+ 2) my hash should have attribute :author match /paul/
59
+ Failure/Error: it { should have_attribute :author, match(/paul/) }
60
+ Expected value of attribute :author to match /paul/ but it was "Paul"
61
+ # ./spec/examples_spec.rb:25:in `block (2 levels) in <top (required)>'
62
+ ```
63
+
64
+ ## `#match_json`
65
+
66
+ This matcher builds upon `#have_attribute` to let you test an entire JSON document in a single example, but still provide detailed errors about each attribute.
67
+
68
+ ### Example Usage
69
+
70
+ ```ruby
71
+ RSpec.describe "my json response document" do
72
+ include RSpec::JsonApiMatchers
73
+
74
+ subject(:response_document) do
75
+ {
76
+ author: "Paul",
77
+ gems_published: 42,
78
+ created_at: "2016-01-01T00:00:00Z"
79
+ }
80
+ end
81
+
82
+ it { should match_json(
83
+ {
84
+ author: "Paul",
85
+ gems_published: be > 40,
86
+ created_at: iso8601_timestamp
87
+ }
88
+ )}
89
+
90
+ end
91
+
92
+ ```
93
+
94
+ Again, it provides good descriptions and useful failure messages:
95
+
96
+ ```
97
+ my json response document
98
+ should have json that looks like
99
+ {
100
+ "author": "Paul",
101
+ "gems_published": be > 40,
102
+ "created_at": match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/
103
+ }
104
+ ```
105
+
106
+ ```
107
+ 1) my json response document should have json that looks like
108
+ {
109
+ "author": "Someone else",
110
+ "gems_published": be > 40,
111
+ "created_at": match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/,
112
+ "does_not_exist": be present
113
+ }
114
+ Failure/Error:
115
+ it { should match_json(
116
+ {
117
+ author: "Paul",
118
+ gems_published: be > 40,
119
+ created_at: iso8601_timestamp,
120
+ does_not_exist: be_present,
121
+ author: "Someone else"
122
+ }
123
+ )}
124
+
125
+ Expected:
126
+ {
127
+ "author": "Paul",
128
+ "gems_published": 42,
129
+ "created_at": "2016-01-01T00:00:00Z"
130
+ }
131
+ To match:
132
+ {
133
+ "author": "Someone else",
134
+ "gems_published": be > 40,
135
+ "created_at": match /\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/,
136
+ "does_not_exist": be present
137
+ }
138
+ Failures:
139
+ {
140
+ "author": Expected value of attribute "author" to eq "Someone else" but it was "Paul",
141
+ "does_not_exist": Expected value of attribute "does_not_exist" to be present but it was nil
142
+ }
143
+ ```
144
+
145
+
146
+ # Installation
147
+
148
+ Add this line to your application's Gemfile:
149
+
150
+ ```ruby
151
+ gem 'rspec-json_api_matchers'
152
+ ```
153
+
154
+ And then execute:
155
+
156
+ $ bundle
157
+
158
+ Or install it yourself as:
159
+
160
+ $ gem install rspec-json_api_matchers
161
+
162
+ # Contributing
163
+
164
+ Bug reports and pull requests are welcome on GitHub at https://github.com/paul/rspec-json_api_matchers.
165
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,22 @@
1
+ require "rspec/json_api_matchers/version"
2
+
3
+ module RSpec
4
+ module JsonApiMatchers
5
+ autoload :AttributeMatcher, "rspec/json_api_matchers/attribute_matcher"
6
+ autoload :JsonMatcher, "rspec/json_api_matchers/json_matcher"
7
+ autoload :Helpers, "rspec/json_api_matchers/helpers"
8
+
9
+ def iso8601_timestamp
10
+ match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z/)
11
+ end
12
+
13
+ def match_json(*a)
14
+ JsonMatcher.new(*a)
15
+ end
16
+
17
+ def have_attribute(*a)
18
+ AttributeMatcher.new(*a)
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ require "active_support/core_ext/hash/indifferent_access"
2
+
3
+ module RSpec::JsonApiMatchers
4
+
5
+ class AttributeMatcher
6
+ include RSpec::JsonApiMatchers::Helpers
7
+
8
+ attr_reader :attribute_name, :expected, :document
9
+
10
+ def initialize(attribute_name, expected = NullMatcher)
11
+ @attribute_name, @expected = attribute_name, expected
12
+ end
13
+
14
+ def description
15
+ sentencize "have attribute #{attribute_name.inspect} #{expected.description}"
16
+ end
17
+
18
+ def matches?(document)
19
+ @document = document.with_indifferent_access
20
+
21
+ @document.key?(attribute_name) &&
22
+ expected.matches?(@document.fetch(attribute_name, nil))
23
+ end
24
+
25
+ def failure_message
26
+ if expected === NullMatcher
27
+ msgs = ["Expected attribute",
28
+ attribute_name.inspect,
29
+ "to be present"]
30
+ else
31
+ msgs = ["Expected value of attribute",
32
+ attribute_name.inspect,
33
+ "to",
34
+ expected.description,
35
+ "but it was",
36
+ document[attribute_name].inspect]
37
+ end
38
+ sentencize(*msgs)
39
+ end
40
+
41
+ def failure_message_when_negated
42
+ sentencize "Expected attribute #{attribute_name.inspect}",
43
+ expected.description,
44
+ "to be absent"
45
+ end
46
+
47
+ NullMatcher = Class.new do
48
+ def matches?(*_args)
49
+ true
50
+ end
51
+
52
+ def failure_message
53
+ ""
54
+ end
55
+ alias_method :failure_message_for_should, :failure_message
56
+
57
+ def description
58
+ "be present"
59
+ end
60
+
61
+ def ===(_other)
62
+ true
63
+ end
64
+ end.new
65
+
66
+ def matcherize(expected)
67
+ if matcher? expected
68
+ expected
69
+
70
+ elsif expected.respond_to? :===
71
+ RSpec::Matchers::Builtin::Match.new(expected)
72
+
73
+ else
74
+ RSpec::Matchers::Builtin::Eq.new(expected)
75
+ end
76
+ end
77
+
78
+ def matcher?(obj)
79
+ obj.respond_to(:matches?) && (obj.respond_to?(:failure_message) ||
80
+ obj.respond_to?(:failure_message_for_should))
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,15 @@
1
+ module RSpec::JsonApiMatchers
2
+ module Helpers
3
+ # Returns string composed of the specified clauses with proper
4
+ # spacing between them. Empty and nil clauses are ignored.
5
+ def sentencize(*clauses)
6
+ clauses
7
+ .flatten
8
+ .compact
9
+ .reject(&:empty?)
10
+ .map(&:strip)
11
+ .join(" ")
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,85 @@
1
+ require "active_support/core_ext/string/indent" # indent
2
+ require "active_support/core_ext/hash/keys" # stringify_keys
3
+
4
+ require "json"
5
+
6
+ module RSpec::JsonApiMatchers
7
+ class JsonMatcher
8
+ include RSpec::Matchers::Composable
9
+ include RSpec::JsonApiMatchers::Helpers
10
+
11
+ def initialize(expected_json)
12
+ @expected_json = expected_json.stringify_keys
13
+ @failed_matchers = {}
14
+ end
15
+
16
+ def matches?(actual_json)
17
+ @actual_json = actual_json
18
+ expected_matchers.each do |expected_key, value_matcher|
19
+ attr_matcher = RSpec::JsonApiMatchers::AttributeMatcher.new(expected_key, value_matcher)
20
+ match = attr_matcher.matches?(@actual_json)
21
+ @failed_matchers[expected_key] = attr_matcher unless match
22
+ end
23
+ @failed_matchers.size == 0
24
+ end
25
+
26
+ def description
27
+ # TODO Figure out how to discover the right indent level
28
+ "have json that looks like\n#{pretty_expected.indent(6)}"
29
+ end
30
+
31
+ def failure_message
32
+ msgs = [ "Expected:",
33
+ pretty_actual.indent(2),
34
+ "To match:",
35
+ pretty_expected.indent(2),
36
+ "Failures:",
37
+ pretty_errors.indent(2) ]
38
+ msgs.join("\n")
39
+ end
40
+
41
+ def pretty_json(obj)
42
+ JSON.pretty_generate(obj)
43
+ end
44
+
45
+ def pretty_expected
46
+ pretty_json(@expected_json)
47
+ end
48
+
49
+ def pretty_actual
50
+ pretty_json(@actual_json)
51
+ end
52
+
53
+ def expected_matchers
54
+ @expected_matchers ||= {}.tap do |hsh|
55
+ @expected_json.each do |k,v|
56
+ hsh[k] = v.respond_to?(:description) ? v : RSpec::Matchers::BuiltIn::Eq.new(v)
57
+ end
58
+ end
59
+ end
60
+
61
+ def pretty_expected
62
+ out = "{\n"
63
+ out << expected_matchers.map do |k,v|
64
+ case v
65
+ when RSpec::Matchers::BuiltIn::Eq
66
+ %{"#{k}": #{v.expected_formatted}}.indent(2)
67
+ else
68
+ %{"#{k}": #{v.description}}.indent(2)
69
+ end
70
+ end.join(",\n")
71
+ out << "\n}"
72
+ end
73
+
74
+ def pretty_errors
75
+ out = "{\n"
76
+ out << @failed_matchers.map do |k,v|
77
+ %{"#{k}": #{v.failure_message}}.indent(2)
78
+ end.join(",\n")
79
+ out << "\n}"
80
+ end
81
+
82
+ end
83
+ end
84
+
85
+
@@ -0,0 +1,5 @@
1
+ module Rspec
2
+ module JsonApiMatchers
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rspec/json_api_matchers/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rspec-json_api_matchers"
8
+ spec.version = Rspec::JsonApiMatchers::VERSION
9
+ spec.authors = ["Paul Sadauskas"]
10
+ spec.email = ["psadauskas@gmail.com"]
11
+
12
+ spec.summary = %q{Helpful matchers for comparing JSON documents.}
13
+ spec.homepage = "https://github.com/paul/rspec-json_api_matchers"
14
+
15
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
16
+ # delete this section to allow pushing this gem to any host.
17
+ # if spec.respond_to?(:metadata)
18
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
19
+ # else
20
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
21
+ # end
22
+
23
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_development_dependency "bundler", "~> 1.11"
29
+ spec.add_development_dependency "rake", "~> 10.0"
30
+ spec.add_development_dependency "rspec", "~> 3.0"
31
+
32
+ spec.add_runtime_dependency "rspec", ">= 2.0", "< 4.0.0.a"
33
+ spec.add_runtime_dependency "rspec-expectations", ">= 2.0", "< 4.0.0.a"
34
+ spec.add_runtime_dependency "activesupport", ">= 4.0" # For String#indent
35
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-json_api_matchers
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Paul Sadauskas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: 4.0.0.a
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '2.0'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: 4.0.0.a
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec-expectations
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '2.0'
82
+ - - "<"
83
+ - !ruby/object:Gem::Version
84
+ version: 4.0.0.a
85
+ type: :runtime
86
+ prerelease: false
87
+ version_requirements: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '2.0'
92
+ - - "<"
93
+ - !ruby/object:Gem::Version
94
+ version: 4.0.0.a
95
+ - !ruby/object:Gem::Dependency
96
+ name: activesupport
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '4.0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '4.0'
109
+ description:
110
+ email:
111
+ - psadauskas@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - ".gitignore"
117
+ - ".rspec"
118
+ - ".travis.yml"
119
+ - Gemfile
120
+ - README.md
121
+ - Rakefile
122
+ - lib/rspec/json_api_matchers.rb
123
+ - lib/rspec/json_api_matchers/attribute_matcher.rb
124
+ - lib/rspec/json_api_matchers/helpers.rb
125
+ - lib/rspec/json_api_matchers/json_matcher.rb
126
+ - lib/rspec/json_api_matchers/version.rb
127
+ - rspec-json_api_matchers.gemspec
128
+ homepage: https://github.com/paul/rspec-json_api_matchers
129
+ licenses: []
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.5.1
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: Helpful matchers for comparing JSON documents.
151
+ test_files: []