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,33 @@
|
|
1
|
+
require "jsontest/matchers/be_json_eql"
|
2
|
+
require "jsontest/matchers/include_json"
|
3
|
+
require "jsontest/matchers/have_json_path"
|
4
|
+
require "jsontest/matchers/have_json_type"
|
5
|
+
require "jsontest/matchers/have_json_size"
|
6
|
+
|
7
|
+
module JsonTest
|
8
|
+
module Matchers
|
9
|
+
def be_json_eql(json = nil)
|
10
|
+
JsonTest::Matchers::BeJsonEql.new(json)
|
11
|
+
end
|
12
|
+
|
13
|
+
def include_json(json = nil)
|
14
|
+
JsonTest::Matchers::IncludeJson.new(json)
|
15
|
+
end
|
16
|
+
|
17
|
+
def have_json_path(path)
|
18
|
+
JsonTest::Matchers::HaveJsonPath.new(path)
|
19
|
+
end
|
20
|
+
|
21
|
+
def have_json_type(type)
|
22
|
+
JsonTest::Matchers::HaveJsonType.new(type)
|
23
|
+
end
|
24
|
+
|
25
|
+
def have_json_size(size)
|
26
|
+
JsonTest::Matchers::HaveJsonSize.new(size)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.configure do |config|
|
32
|
+
config.include JsonTest::Matchers
|
33
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Matchers
|
3
|
+
class BeJsonEql
|
4
|
+
include JsonTest::Helpers
|
5
|
+
include JsonTest::Exclusion
|
6
|
+
include JsonTest::Messages
|
7
|
+
|
8
|
+
attr_reader :expected, :actual
|
9
|
+
|
10
|
+
def diffable?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(expected_json = nil)
|
15
|
+
@expected_json = expected_json
|
16
|
+
@path = nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def matches?(actual_json)
|
20
|
+
raise "Expected equivalent JSON not provided" if @expected_json.nil?
|
21
|
+
|
22
|
+
@actual, @expected = scrub(actual_json, @path), scrub(@expected_json)
|
23
|
+
@actual == @expected
|
24
|
+
end
|
25
|
+
|
26
|
+
def at_path(path)
|
27
|
+
@path = path
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_file(path)
|
32
|
+
@expected_json = load_json(path)
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def excluding(*keys)
|
37
|
+
excluded_keys.merge(keys.map(&:to_s))
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def including(*keys)
|
42
|
+
excluded_keys.subtract(keys.map(&:to_s))
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure_message
|
47
|
+
message_with_path("Expected equivalent JSON")
|
48
|
+
end
|
49
|
+
alias :failure_message_for_should :failure_message
|
50
|
+
|
51
|
+
def failure_message_when_negated
|
52
|
+
message_with_path("Expected inequivalent JSON")
|
53
|
+
end
|
54
|
+
alias :failure_message_for_should_not :failure_message_when_negated
|
55
|
+
|
56
|
+
def description
|
57
|
+
message_with_path("equal JSON")
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
def scrub(json, path = nil)
|
62
|
+
generate_normalized_json(exclude_keys(parse_json(json, path))).chomp + "\n"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Matchers
|
3
|
+
class HaveJsonPath
|
4
|
+
include JsonTest::Helpers
|
5
|
+
|
6
|
+
def initialize(path)
|
7
|
+
@path = path
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(json)
|
11
|
+
parse_json(json, @path)
|
12
|
+
true
|
13
|
+
rescue JsonTest::MissingPath
|
14
|
+
false
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure_message
|
18
|
+
%(Expected JSON path "#{@path}")
|
19
|
+
end
|
20
|
+
alias :failure_message_for_should :failure_message
|
21
|
+
|
22
|
+
def failure_message_when_negated
|
23
|
+
%(Expected no JSON path "#{@path}")
|
24
|
+
end
|
25
|
+
alias :failure_message_for_should_not :failure_message_when_negated
|
26
|
+
|
27
|
+
def description
|
28
|
+
%(have JSON path "#{@path}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Matchers
|
3
|
+
class HaveJsonSize
|
4
|
+
include JsonTest::Helpers
|
5
|
+
include JsonTest::Messages
|
6
|
+
|
7
|
+
def initialize(size)
|
8
|
+
@expected = size
|
9
|
+
@path = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(json)
|
13
|
+
ruby = parse_json(json, @path)
|
14
|
+
raise EnumerableExpected.new(ruby) unless Enumerable === ruby
|
15
|
+
@actual = ruby.size
|
16
|
+
@actual == @expected
|
17
|
+
end
|
18
|
+
|
19
|
+
def at_path(path)
|
20
|
+
@path = path
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def failure_message
|
25
|
+
message_with_path("Expected JSON value size to be #{@expected}, got #{@actual}")
|
26
|
+
end
|
27
|
+
alias :failure_message_for_should :failure_message
|
28
|
+
|
29
|
+
def failure_message_when_negated
|
30
|
+
message_with_path("Expected JSON value size to not be #{@expected}, got #{@actual}")
|
31
|
+
end
|
32
|
+
alias :failure_message_for_should_not :failure_message_when_negated
|
33
|
+
|
34
|
+
def description
|
35
|
+
message_with_path(%(have JSON size "#{@expected}"))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Matchers
|
3
|
+
class HaveJsonType
|
4
|
+
include JsonTest::Helpers
|
5
|
+
include JsonTest::Messages
|
6
|
+
|
7
|
+
def initialize(type)
|
8
|
+
@classes = type_to_classes(type)
|
9
|
+
@path = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def matches?(json)
|
13
|
+
@ruby = parse_json(json, @path)
|
14
|
+
@classes.any? { |c| c === @ruby }
|
15
|
+
end
|
16
|
+
|
17
|
+
def at_path(path)
|
18
|
+
@path = path
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def failure_message
|
23
|
+
message_with_path("Expected JSON value type to be #{@classes.join(", ")}, got #{@ruby.class}")
|
24
|
+
end
|
25
|
+
alias :failure_message_for_should :failure_message
|
26
|
+
|
27
|
+
def failure_message_when_negated
|
28
|
+
message_with_path("Expected JSON value type to not be #{@classes.join(", ")}, got #{@ruby.class}")
|
29
|
+
end
|
30
|
+
alias :failure_message_for_should_not :failure_message_when_negated
|
31
|
+
|
32
|
+
def description
|
33
|
+
message_with_path(%(have JSON type "#{@classes.join(", ")}"))
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def type_to_classes(type)
|
38
|
+
case type
|
39
|
+
when Class then [type]
|
40
|
+
when Array then type.map { |t| type_to_classes(t) }.flatten
|
41
|
+
else
|
42
|
+
case type.to_s.downcase
|
43
|
+
when "boolean" then [TrueClass, FalseClass]
|
44
|
+
when "object" then [Hash]
|
45
|
+
when "nil", "null" then [NilClass]
|
46
|
+
else [Module.const_get(type.to_s.capitalize)]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Matchers
|
3
|
+
class IncludeJson
|
4
|
+
include JsonTest::Helpers
|
5
|
+
include JsonTest::Exclusion
|
6
|
+
include JsonTest::Messages
|
7
|
+
|
8
|
+
def initialize(expected_json = nil)
|
9
|
+
@expected_json = expected_json
|
10
|
+
@path = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def matches?(actual_json)
|
14
|
+
raise "Expected included JSON not provided" if @expected_json.nil?
|
15
|
+
|
16
|
+
@actual_json = actual_json
|
17
|
+
|
18
|
+
actual = parse_json(actual_json, @path)
|
19
|
+
expected = exclude_keys(parse_json(@expected_json))
|
20
|
+
case actual
|
21
|
+
when Hash then actual.values.map { |v| exclude_keys(v) }.include?(expected)
|
22
|
+
when Array then actual.map { |e| exclude_keys(e) }.include?(expected)
|
23
|
+
when String then actual.include?(expected)
|
24
|
+
else false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def at_path(path)
|
29
|
+
@path = path
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def from_file(path)
|
34
|
+
@expected_json = load_json(path)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def excluding(*keys)
|
39
|
+
excluded_keys.merge(keys.map(&:to_s))
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def including(*keys)
|
44
|
+
excluded_keys.subtract(keys.map(&:to_s))
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def failure_message
|
49
|
+
message_with_path("Expected #{@actual_json} to include #{@expected_json}")
|
50
|
+
end
|
51
|
+
alias :failure_message_for_should :failure_message
|
52
|
+
|
53
|
+
def failure_message_when_negated
|
54
|
+
message_with_path("Expected #{@actual_json} to not include #{@expected_json}")
|
55
|
+
end
|
56
|
+
alias :failure_message_for_should_not :failure_message_when_negated
|
57
|
+
|
58
|
+
def description
|
59
|
+
message_with_path("include JSON")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module JsonTest
|
2
|
+
module Memory
|
3
|
+
def memory
|
4
|
+
@memory ||= {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def memorize(key, value)
|
8
|
+
memory[key.to_sym] = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def remember(json)
|
12
|
+
memory.empty? ? json : json % memory
|
13
|
+
end
|
14
|
+
|
15
|
+
def forget
|
16
|
+
memory.clear
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JsonTest::Configuration do
|
4
|
+
it "excludes id and timestamps by default" do
|
5
|
+
JsonTest.excluded_keys.should eq ["id", "created_at", "updated_at"]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "excludes custom keys" do
|
9
|
+
JsonTest.exclude_keys("token")
|
10
|
+
JsonTest.excluded_keys.should eq ["token"]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "excludes custom keys via setter" do
|
14
|
+
JsonTest.excluded_keys = ["token"]
|
15
|
+
JsonTest.excluded_keys.should eq ["token"]
|
16
|
+
end
|
17
|
+
|
18
|
+
it "excludes custom keys via block" do
|
19
|
+
JsonTest.configure { |c| c.exclude_keys("token") }
|
20
|
+
JsonTest.excluded_keys.should eq ["token"]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "excludes custom keys via block setter" do
|
24
|
+
JsonTest.configure { |c| c.excluded_keys = ["token"] }
|
25
|
+
JsonTest.excluded_keys.should eq ["token"]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "excludes custom keys via instance-evaluated block" do
|
29
|
+
JsonTest.configure{ exclude_keys("token") }
|
30
|
+
JsonTest.excluded_keys.should eq ["token"]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "ensures its excluded keys are strings" do
|
34
|
+
JsonTest.exclude_keys(:token)
|
35
|
+
JsonTest.excluded_keys.should eq ["token"]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "ensures its excluded keys are unique" do
|
39
|
+
JsonTest.exclude_keys("token", :token)
|
40
|
+
JsonTest.excluded_keys.should eq ["token"]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "resets its excluded keys" do
|
44
|
+
original = JsonTest.excluded_keys
|
45
|
+
|
46
|
+
JsonTest.exclude_keys("token")
|
47
|
+
JsonTest.excluded_keys.should_not eq original
|
48
|
+
|
49
|
+
JsonTest.reset
|
50
|
+
JsonTest.excluded_keys.should eq original
|
51
|
+
end
|
52
|
+
|
53
|
+
it "resets its directory" do
|
54
|
+
JsonTest.directory.should be_nil
|
55
|
+
|
56
|
+
JsonTest.directory = "/"
|
57
|
+
JsonTest.directory.should_not be_nil
|
58
|
+
|
59
|
+
JsonTest.reset
|
60
|
+
JsonTest.directory.should be_nil
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe JsonTest::Helpers do
|
4
|
+
include described_class
|
5
|
+
|
6
|
+
context "parse_json" do
|
7
|
+
it "parses JSON documents" do
|
8
|
+
parse_json(%({"json":["spec"]})).should eq({"json" => ["spec"]})
|
9
|
+
end
|
10
|
+
|
11
|
+
it "parses JSON values" do
|
12
|
+
parse_json(%("jsontest")).should eq "jsontest"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "raises a parser error for invalid JSON" do
|
16
|
+
expect{ parse_json("jsontest") }.to raise_error(MultiJson::DecodeError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "parses at a path if given" do
|
20
|
+
json = %({"json":["spec"]})
|
21
|
+
parse_json(json, "json").should eq ["spec"]
|
22
|
+
parse_json(json, "json/0").should eq "spec"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises an error for a missing path" do
|
26
|
+
json = %({"json":["spec"]})
|
27
|
+
%w(spec json/1).each do |path|
|
28
|
+
expect{ parse_json(json, path) }.to raise_error(JsonTest::MissingPath)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it "parses at a numeric string path" do
|
33
|
+
json = %({"1":"two"})
|
34
|
+
parse_json(json, "1").should eq "two"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "normalize_json" do
|
39
|
+
it "normalizes a JSON document" do
|
40
|
+
normalized = <<-JSON
|
41
|
+
{
|
42
|
+
"json": [
|
43
|
+
"spec"
|
44
|
+
]
|
45
|
+
}
|
46
|
+
JSON
|
47
|
+
normalize_json(%({"json":["spec"]})).should eq normalized.chomp
|
48
|
+
end
|
49
|
+
|
50
|
+
it "normalizes at a path" do
|
51
|
+
normalize_json(%({"json":["spec"]}), "json/0").should eq %("spec")
|
52
|
+
end
|
53
|
+
|
54
|
+
it "accepts a JSON value" do
|
55
|
+
normalize_json(%("jsontest")).should eq %("jsontest")
|
56
|
+
end
|
57
|
+
|
58
|
+
it "normalizes JSON values" do
|
59
|
+
normalize_json(%(1e+1)).should eq %(10.0)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "generate_normalized_json" do
|
64
|
+
it "generates a normalized JSON document" do
|
65
|
+
normalized = <<-JSON
|
66
|
+
{
|
67
|
+
"json": [
|
68
|
+
"spec"
|
69
|
+
]
|
70
|
+
}
|
71
|
+
JSON
|
72
|
+
generate_normalized_json({"json" => ["spec"]}).should eq normalized.chomp
|
73
|
+
end
|
74
|
+
|
75
|
+
it "generates a normalized JSON value" do
|
76
|
+
generate_normalized_json(nil).should eq %(null)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "load_json_file" do
|
81
|
+
it "raises an error when no directory is set" do
|
82
|
+
expect{ load_json("one.json") }.to raise_error(JsonTest::MissingDirectory)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "returns JSON when the file exists" do
|
86
|
+
JsonTest.directory = files_path
|
87
|
+
load_json("one.json").should eq %({"value":"from_file"})
|
88
|
+
end
|
89
|
+
|
90
|
+
it "ignores extra slashes" do
|
91
|
+
JsonTest.directory = "/#{files_path}/"
|
92
|
+
load_json("one.json").should eq %({"value":"from_file"})
|
93
|
+
end
|
94
|
+
|
95
|
+
it "raises an error when the file doesn't exist" do
|
96
|
+
JsonTest.directory = files_path
|
97
|
+
expect{ load_json("bogus.json") }.to raise_error(JsonTest::MissingFile)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises an error when the directory doesn't exist" do
|
101
|
+
JsonTest.directory = "#{files_path}_bogus"
|
102
|
+
expect{ load_json("one.json") }.to raise_error(JsonTest::MissingFile)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "finds nested files" do
|
106
|
+
JsonTest.directory = files_path
|
107
|
+
load_json("project/one.json").should eq %({"nested":"inside_folder"})
|
108
|
+
load_json("project/version/one.json").should eq %({"nested":"deeply"})
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|