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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +21 -0
  4. data/CHANGELOG.md +8 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +343 -0
  8. data/Rakefile +18 -0
  9. data/features/equivalence.feature +286 -0
  10. data/features/files.feature +89 -0
  11. data/features/inclusion.feature +154 -0
  12. data/features/memory.feature +221 -0
  13. data/features/paths.feature +74 -0
  14. data/features/sizes.feature +38 -0
  15. data/features/step_definitions/steps.rb +7 -0
  16. data/features/support/env.rb +9 -0
  17. data/features/types.feature +24 -0
  18. data/gemfiles/rspec2.gemfile +10 -0
  19. data/gemfiles/rspec3.gemfile +10 -0
  20. data/jsontest.gemspec +22 -0
  21. data/lib/jsontest.rb +14 -0
  22. data/lib/jsontest/configuration.rb +30 -0
  23. data/lib/jsontest/cucumber.rb +95 -0
  24. data/lib/jsontest/errors.rb +46 -0
  25. data/lib/jsontest/exclusion.rb +26 -0
  26. data/lib/jsontest/helpers.rb +62 -0
  27. data/lib/jsontest/matchers.rb +33 -0
  28. data/lib/jsontest/matchers/be_json_eql.rb +66 -0
  29. data/lib/jsontest/matchers/have_json_path.rb +32 -0
  30. data/lib/jsontest/matchers/have_json_size.rb +39 -0
  31. data/lib/jsontest/matchers/have_json_type.rb +52 -0
  32. data/lib/jsontest/matchers/include_json.rb +63 -0
  33. data/lib/jsontest/memory.rb +19 -0
  34. data/lib/jsontest/messages.rb +8 -0
  35. data/spec/jsontest/configuration_spec.rb +62 -0
  36. data/spec/jsontest/helpers_spec.rb +111 -0
  37. data/spec/jsontest/matchers/be_json_eql_spec.rb +111 -0
  38. data/spec/jsontest/matchers/have_json_path_spec.rb +29 -0
  39. data/spec/jsontest/matchers/have_json_size_spec.rb +61 -0
  40. data/spec/jsontest/matchers/have_json_type_spec.rb +92 -0
  41. data/spec/jsontest/matchers/include_json_spec.rb +96 -0
  42. data/spec/jsontest/matchers_spec.rb +77 -0
  43. data/spec/jsontest/memory_spec.rb +32 -0
  44. data/spec/spec_helper.rb +23 -0
  45. data/spec/support/files/one.json +1 -0
  46. data/spec/support/files/project/one.json +1 -0
  47. data/spec/support/files/project/two.json +18 -0
  48. data/spec/support/files/project/version/one.json +1 -0
  49. data/spec/support/files/project/version/two.json +3 -0
  50. data/spec/support/files/two.json +24 -0
  51. 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,7 @@
1
+ Given "the JSON is:" do |json|
2
+ @json = json
3
+ end
4
+
5
+ When "I get the JSON" do
6
+ @last_json = @json
7
+ end
@@ -0,0 +1,9 @@
1
+ $: << File.expand_path("../../../lib", __FILE__)
2
+
3
+ require "jsontest/cucumber"
4
+
5
+ JsonTest.directory = File.expand_path("../../../spec/support/files", __FILE__)
6
+
7
+ def last_json
8
+ @last_json.to_s
9
+ end
@@ -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
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rspec", "~> 2.0"
6
+
7
+ group :test do
8
+ gem "codeclimate-test-reporter", "~> 1.0.0"
9
+ gem "cucumber", "~> 1.3"
10
+ end
@@ -0,0 +1,10 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec path: ".."
4
+
5
+ gem "rspec", "~> 3.0"
6
+
7
+ group :test do
8
+ gem "codeclimate-test-reporter", "~> 1.0.0"
9
+ gem "cucumber", "~> 1.3"
10
+ end
@@ -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
@@ -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