jsontest 1.0.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 +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +21 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +20 -0
- data/README.md +343 -0
- data/Rakefile +18 -0
- data/features/equivalence.feature +286 -0
- data/features/files.feature +89 -0
- data/features/inclusion.feature +154 -0
- data/features/memory.feature +221 -0
- data/features/paths.feature +74 -0
- data/features/sizes.feature +38 -0
- data/features/step_definitions/steps.rb +7 -0
- data/features/support/env.rb +9 -0
- data/features/types.feature +24 -0
- data/gemfiles/rspec2.gemfile +10 -0
- data/gemfiles/rspec3.gemfile +10 -0
- data/jsontest.gemspec +22 -0
- data/lib/jsontest.rb +14 -0
- data/lib/jsontest/configuration.rb +30 -0
- data/lib/jsontest/cucumber.rb +95 -0
- data/lib/jsontest/errors.rb +46 -0
- data/lib/jsontest/exclusion.rb +26 -0
- data/lib/jsontest/helpers.rb +62 -0
- data/lib/jsontest/matchers.rb +33 -0
- data/lib/jsontest/matchers/be_json_eql.rb +66 -0
- data/lib/jsontest/matchers/have_json_path.rb +32 -0
- data/lib/jsontest/matchers/have_json_size.rb +39 -0
- data/lib/jsontest/matchers/have_json_type.rb +52 -0
- data/lib/jsontest/matchers/include_json.rb +63 -0
- data/lib/jsontest/memory.rb +19 -0
- data/lib/jsontest/messages.rb +8 -0
- data/spec/jsontest/configuration_spec.rb +62 -0
- data/spec/jsontest/helpers_spec.rb +111 -0
- data/spec/jsontest/matchers/be_json_eql_spec.rb +111 -0
- data/spec/jsontest/matchers/have_json_path_spec.rb +29 -0
- data/spec/jsontest/matchers/have_json_size_spec.rb +61 -0
- data/spec/jsontest/matchers/have_json_type_spec.rb +92 -0
- data/spec/jsontest/matchers/include_json_spec.rb +96 -0
- data/spec/jsontest/matchers_spec.rb +77 -0
- data/spec/jsontest/memory_spec.rb +32 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/files/one.json +1 -0
- data/spec/support/files/project/one.json +1 -0
- data/spec/support/files/project/two.json +18 -0
- data/spec/support/files/project/version/one.json +1 -0
- data/spec/support/files/project/version/two.json +3 -0
- data/spec/support/files/two.json +24 -0
- metadata +182 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
Feature: Paths
|
2
|
+
Background:
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"array": [
|
7
|
+
{
|
8
|
+
"one": 1,
|
9
|
+
"two": 2
|
10
|
+
},
|
11
|
+
{
|
12
|
+
"four": 4,
|
13
|
+
"three": 3
|
14
|
+
}
|
15
|
+
],
|
16
|
+
"hash": {
|
17
|
+
"even": [
|
18
|
+
6,
|
19
|
+
8
|
20
|
+
],
|
21
|
+
"odd": [
|
22
|
+
5,
|
23
|
+
7
|
24
|
+
]
|
25
|
+
},
|
26
|
+
"id": null
|
27
|
+
}
|
28
|
+
"""
|
29
|
+
|
30
|
+
Scenario: Base paths
|
31
|
+
When I get the JSON
|
32
|
+
Then the JSON should have "array"
|
33
|
+
And the JSON should have "hash"
|
34
|
+
And the JSON should have "id"
|
35
|
+
|
36
|
+
Scenario: Nested paths
|
37
|
+
When I get the JSON
|
38
|
+
Then the JSON should have "array/0"
|
39
|
+
And the JSON should have "array/1"
|
40
|
+
And the JSON should have "hash/even"
|
41
|
+
And the JSON should have "hash/odd"
|
42
|
+
|
43
|
+
Scenario: Deeply nested paths
|
44
|
+
When I get the JSON
|
45
|
+
Then the JSON should have "array/0/one"
|
46
|
+
And the JSON should have "array/0/two"
|
47
|
+
And the JSON should have "array/1/four"
|
48
|
+
And the JSON should have "array/1/three"
|
49
|
+
And the JSON should have "hash/even/0"
|
50
|
+
And the JSON should have "hash/even/1"
|
51
|
+
And the JSON should have "hash/odd/0"
|
52
|
+
And the JSON should have "hash/odd/1"
|
53
|
+
|
54
|
+
Scenario: Ignored paths
|
55
|
+
When I get the JSON
|
56
|
+
Then the JSON should have "id"
|
57
|
+
|
58
|
+
Scenario: Table format
|
59
|
+
When I get the JSON
|
60
|
+
Then the JSON should have the following:
|
61
|
+
| array |
|
62
|
+
| hash |
|
63
|
+
| array/0 |
|
64
|
+
| array/1 |
|
65
|
+
| hash/even |
|
66
|
+
| hash/odd |
|
67
|
+
| array/0/one |
|
68
|
+
| array/0/two |
|
69
|
+
| array/1/four |
|
70
|
+
| array/1/three |
|
71
|
+
| hash/even/0 |
|
72
|
+
| hash/even/1 |
|
73
|
+
| hash/odd/0 |
|
74
|
+
| hash/odd/1 |
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Feature: Sizes
|
2
|
+
Background:
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"id": null,
|
7
|
+
"one": [
|
8
|
+
1
|
9
|
+
],
|
10
|
+
"three": [
|
11
|
+
1,
|
12
|
+
2,
|
13
|
+
3
|
14
|
+
],
|
15
|
+
"two": [
|
16
|
+
1,
|
17
|
+
2
|
18
|
+
],
|
19
|
+
"zero": [
|
20
|
+
|
21
|
+
]
|
22
|
+
}
|
23
|
+
"""
|
24
|
+
|
25
|
+
Scenario: Hash
|
26
|
+
When I get the JSON
|
27
|
+
Then the JSON should have 5 keys
|
28
|
+
And the JSON should have 5 values
|
29
|
+
|
30
|
+
Scenario: Empty array
|
31
|
+
When I get the JSON
|
32
|
+
Then the JSON at "zero" should have 0 entries
|
33
|
+
|
34
|
+
Scenario: Array
|
35
|
+
When I get the JSON
|
36
|
+
Then the JSON at "one" should have 1 entry
|
37
|
+
And the JSON at "two" should have 2 values
|
38
|
+
And the JSON at "three" should have 3 numbers
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Types
|
2
|
+
Scenario: All types
|
3
|
+
Given the JSON is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"array": [],
|
7
|
+
"false": true,
|
8
|
+
"float": 10.0,
|
9
|
+
"hash": {},
|
10
|
+
"integer": 10,
|
11
|
+
"string": "jsontest",
|
12
|
+
"true": true
|
13
|
+
}
|
14
|
+
"""
|
15
|
+
When I get the JSON
|
16
|
+
Then the JSON should be a hash
|
17
|
+
And the JSON at "array" should be an array
|
18
|
+
And the JSON at "false" should be a boolean
|
19
|
+
And the JSON at "float" should be a float
|
20
|
+
And the JSON at "hash" should be a hash
|
21
|
+
And the JSON at "hash" should be an object
|
22
|
+
And the JSON at "integer" should be an integer
|
23
|
+
And the JSON at "string" should be a string
|
24
|
+
And the JSON at "true" should be a boolean
|
data/jsontest.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "jsontest"
|
5
|
+
gem.version = "1.0.0"
|
6
|
+
|
7
|
+
gem.authors = ["Steve Richert", "Michiel Boekhoff"]
|
8
|
+
gem.email = ["steve.richert@gmail.com", "michielalexanderb@gmail.com"]
|
9
|
+
gem.summary = "Easily test JSON in RSpec and Cucumber"
|
10
|
+
gem.description = "RSpec matchers and Cucumber steps for testing JSON content"
|
11
|
+
gem.homepage = "https://github.com/michielboekhoff/jsontest"
|
12
|
+
gem.license = "MIT"
|
13
|
+
|
14
|
+
gem.add_dependency "multi_json", "~> 1.0"
|
15
|
+
gem.add_dependency "rspec", ">= 2.0", "< 4.0"
|
16
|
+
|
17
|
+
gem.add_development_dependency "bundler", "~> 1.0"
|
18
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
19
|
+
|
20
|
+
gem.files = `git ls-files`.split($\)
|
21
|
+
gem.test_files = gem.files.grep(/^(features|spec)/)
|
22
|
+
end
|
data/lib/jsontest.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require "json"
|
2
|
+
require "rspec"
|
3
|
+
require "jsontest/errors"
|
4
|
+
require "jsontest/configuration"
|
5
|
+
require "jsontest/exclusion"
|
6
|
+
require "jsontest/helpers"
|
7
|
+
require "jsontest/messages"
|
8
|
+
require "jsontest/matchers"
|
9
|
+
require "jsontest/memory"
|
10
|
+
|
11
|
+
module JsonTest
|
12
|
+
extend Configuration
|
13
|
+
extend Memory
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "set"
|
2
|
+
|
3
|
+
module JsonTest
|
4
|
+
module Configuration
|
5
|
+
DEFAULT_EXCLUDED_KEYS = %w(id created_at updated_at)
|
6
|
+
|
7
|
+
attr_accessor :directory
|
8
|
+
|
9
|
+
def configure(&block)
|
10
|
+
instance_eval(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def excluded_keys
|
14
|
+
@excluded_keys ||= DEFAULT_EXCLUDED_KEYS
|
15
|
+
end
|
16
|
+
|
17
|
+
def excluded_keys=(keys)
|
18
|
+
@excluded_keys = keys.map(&:to_s).uniq
|
19
|
+
end
|
20
|
+
|
21
|
+
def exclude_keys(*keys)
|
22
|
+
self.excluded_keys = keys
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def reset
|
27
|
+
instance_variables.each { |ivar| remove_instance_variable(ivar) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.expand_path("../../jsontest", __FILE__)
|
2
|
+
|
3
|
+
World(JsonTest::Helpers, JsonTest::Matchers)
|
4
|
+
|
5
|
+
After do
|
6
|
+
JsonTest.forget
|
7
|
+
end
|
8
|
+
|
9
|
+
When /^(?:I )?keep the (?:JSON|json)(?: response)?(?: at "(.*)")? as "(.*)"$/ do |path, key|
|
10
|
+
JsonTest.memorize(key, normalize_json(last_json, path))
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be:$/ do |path, negative, json|
|
14
|
+
if negative
|
15
|
+
last_json.should_not be_json_eql(JsonTest.remember(json)).at_path(path)
|
16
|
+
else
|
17
|
+
last_json.should be_json_eql(JsonTest.remember(json)).at_path(path)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be file "(.+)"$/ do |path, negative, file_path|
|
22
|
+
if negative
|
23
|
+
last_json.should_not be_json_eql.to_file(file_path).at_path(path)
|
24
|
+
else
|
25
|
+
last_json.should be_json_eql.to_file(file_path).at_path(path)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ do |path, negative, value|
|
30
|
+
if negative
|
31
|
+
last_json.should_not be_json_eql(JsonTest.remember(value)).at_path(path)
|
32
|
+
else
|
33
|
+
last_json.should be_json_eql(JsonTest.remember(value)).at_path(path)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include:$/ do |path, negative, json|
|
38
|
+
if negative
|
39
|
+
last_json.should_not include_json(JsonTest.remember(json)).at_path(path)
|
40
|
+
else
|
41
|
+
last_json.should include_json(JsonTest.remember(json)).at_path(path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include file "(.+)"$/ do |path, negative, file_path|
|
46
|
+
if negative
|
47
|
+
last_json.should_not include_json.from_file(file_path).at_path(path)
|
48
|
+
else
|
49
|
+
last_json.should include_json.from_file(file_path).at_path(path)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? include (".*"|\-?\d+(?:\.\d+)?(?:[eE][\+\-]?\d+)?|\[.*\]|%?\{.*\}|true|false|null)$/ do |path, negative, value|
|
54
|
+
if negative
|
55
|
+
last_json.should_not include_json(JsonTest.remember(value)).at_path(path)
|
56
|
+
else
|
57
|
+
last_json.should include_json(JsonTest.remember(value)).at_path(path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should have the following:$/ do |base, table|
|
62
|
+
table.raw.each do |path, value|
|
63
|
+
path = [base, path].compact.join("/")
|
64
|
+
|
65
|
+
if value
|
66
|
+
step %(the JSON at "#{path}" should be:), value
|
67
|
+
else
|
68
|
+
step %(the JSON should have "#{path}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
Then /^the (?:JSON|json)(?: response)? should( not)? have "(.*)"$/ do |negative, path|
|
74
|
+
if negative
|
75
|
+
last_json.should_not have_json_path(path)
|
76
|
+
else
|
77
|
+
last_json.should have_json_path(path)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? be an? (.*)$/ do |path, negative, type|
|
82
|
+
if negative
|
83
|
+
last_json.should_not have_json_type(type).at_path(path)
|
84
|
+
else
|
85
|
+
last_json.should have_json_type(type).at_path(path)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Then /^the (?:JSON|json)(?: response)?(?: at "(.*)")? should( not)? have (\d+)/ do |path, negative, size|
|
90
|
+
if negative
|
91
|
+
last_json.should_not have_json_size(size.to_i).at_path(path)
|
92
|
+
else
|
93
|
+
last_json.should have_json_size(size.to_i).at_path(path)
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module JsonTest
|
2
|
+
class Error < StandardError
|
3
|
+
end
|
4
|
+
|
5
|
+
class MissingPath < Error
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
%(Missing JSON path "#{path}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class MissingDirectory < Error
|
18
|
+
def to_s
|
19
|
+
"No JsonTest.directory set"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class MissingFile < Error
|
24
|
+
attr_reader :path
|
25
|
+
|
26
|
+
def initialize(path)
|
27
|
+
@path = path
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"No JSON file at #{path}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class EnumerableExpected < Error
|
36
|
+
attr_reader :actual_value
|
37
|
+
|
38
|
+
def initialize(actual_value)
|
39
|
+
@actual_value = actual_value
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
"Enumerable expected, got #{actual_value.inspect}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Exclusion
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def exclude_keys(ruby)
|
6
|
+
case ruby
|
7
|
+
when Hash
|
8
|
+
ruby.sort.inject({}) do |hash, (key, value)|
|
9
|
+
hash[key] = exclude_keys(value) unless exclude_key?(key)
|
10
|
+
hash
|
11
|
+
end
|
12
|
+
when Array
|
13
|
+
ruby.map{|v| exclude_keys(v) }
|
14
|
+
else ruby
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def exclude_key?(key)
|
19
|
+
excluded_keys.include?(key)
|
20
|
+
end
|
21
|
+
|
22
|
+
def excluded_keys
|
23
|
+
@excluded_keys ||= Set.new(JsonTest.excluded_keys)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "multi_json"
|
2
|
+
|
3
|
+
module JsonTest
|
4
|
+
module Helpers
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def parse_json(json, path = nil)
|
8
|
+
ruby = MultiJson.decode("[#{json}]").first
|
9
|
+
value_at_json_path(ruby, path)
|
10
|
+
rescue MultiJson::DecodeError
|
11
|
+
MultiJson.decode(json)
|
12
|
+
end
|
13
|
+
|
14
|
+
def normalize_json(json, path = nil)
|
15
|
+
ruby = parse_json(json, path)
|
16
|
+
generate_normalized_json(ruby)
|
17
|
+
end
|
18
|
+
|
19
|
+
def generate_normalized_json(ruby)
|
20
|
+
case ruby
|
21
|
+
when Hash, Array then JSON.pretty_generate(ruby)
|
22
|
+
else ruby.to_json
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_json(relative_path)
|
27
|
+
missing_json_directory! if JsonTest.directory.nil?
|
28
|
+
path = File.join(JsonTest.directory, relative_path)
|
29
|
+
missing_json_file!(path) unless File.exist?(path)
|
30
|
+
File.read(path)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
def value_at_json_path(ruby, path)
|
35
|
+
return ruby unless path
|
36
|
+
|
37
|
+
path.split("/").inject(ruby) do |value, key|
|
38
|
+
case value
|
39
|
+
when Hash
|
40
|
+
value.fetch(key){ missing_json_path!(path) }
|
41
|
+
when Array
|
42
|
+
missing_json_path!(path) unless key =~ /^\d+$/
|
43
|
+
value.fetch(key.to_i){ missing_json_path!(path) }
|
44
|
+
else
|
45
|
+
missing_json_path!(path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def missing_json_path!(path)
|
51
|
+
raise JsonTest::MissingPath.new(path)
|
52
|
+
end
|
53
|
+
|
54
|
+
def missing_json_directory!
|
55
|
+
raise JsonTest::MissingDirectory
|
56
|
+
end
|
57
|
+
|
58
|
+
def missing_json_file!(path)
|
59
|
+
raise JsonTest::MissingFile.new(path)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|