Skip to content

Commit

Permalink
Merge pull request #8 from derek-watson/feature/has_only
Browse files Browse the repository at this point in the history
has_only: enforce a fixed sets of JSON object properties
  • Loading branch information
alto committed Apr 20, 2015
2 parents 743ddbe + 74c22ba commit b34011d
Show file tree
Hide file tree
Showing 3 changed files with 151 additions and 1 deletion.
20 changes: 20 additions & 0 deletions README.md
Expand Up @@ -58,6 +58,26 @@ assert_json '[{"id":1, "key":"test", "name":"test"}, {"id":2, "key":"test", "nam
end
```

To test that objects have the declared set of properties and nothing more,
include `has_only` at any level, like this

```ruby
assert_json '[{"id":1, "key":"test", "name":"test"}, {"id":2, "key":"test", "name":"test"}, {"id":3, "key":"test", "name":"test"}]' do
has_only
item 0 do
has :id, 1
has :key, 'test'
has :name, 'test'
end
item 1 do
has 'id', 2
has 'key', 'test'
end
end

# Failure: element 1 has unexpected keys: name
```


## Changelog ##

Expand Down
38 changes: 37 additions & 1 deletion lib/assert_json/assert_json.rb
Expand Up @@ -5,6 +5,7 @@ def assert_json(json_string, &block)
@json = AssertJson::Json.new(json_string)
# json.instance_exec(json, &block)
yield @json
@json.test_for_unexpected_keys('root')
end
end

Expand All @@ -20,19 +21,31 @@ def has_not(*args, &block)
@json.has_not(*args, &block)
end

def has_only
@json.has_only
end

class Json

def initialize(json_string)
@decoded_json = ActiveSupport::JSON.decode(json_string)
@expected_keys = []
@only = false
end

def item(index, &block)
only_in_scope = @only
expected_keys_in_scope = @expected_keys
@expected_keys = []
decoded_json_in_scope = @decoded_json
@decoded_json = @decoded_json[index]
begin
yield if block_given?
test_for_unexpected_keys(index)
ensure
@decoded_json = decoded_json_in_scope
@expected_keys = expected_keys_in_scope
@only = only_in_scope
end
end

Expand Down Expand Up @@ -79,8 +92,13 @@ def element(*args, &block)
flunk
end

@expected_keys.push arg

if block_given?
begin
only_in_scope = @only
expected_keys_in_scope = @expected_keys
@expected_keys = []
decoded_json_in_scope = @decoded_json
@decoded_json = case token
when Hash
Expand All @@ -89,11 +107,13 @@ def element(*args, &block)
token
end
yield
test_for_unexpected_keys(arg)
ensure
@expected_keys = expected_keys_in_scope
@only = only_in_scope
@decoded_json = decoded_json_in_scope
end
end

end
alias has element

Expand All @@ -109,6 +129,22 @@ def not_element(*args, &block)
end
alias has_not not_element

def only
@only = true
end
alias has_only only

def test_for_unexpected_keys(name = 'root')
return unless @only

if @decoded_json.is_a?(Hash)
unexpected_keys = @decoded_json.keys - @expected_keys
if unexpected_keys.count > 0
raise_error("element #{name} has unexpected keys: #{unexpected_keys.join(', ')}")
end
end
end

private

def raise_error(message)
Expand Down
94 changes: 94 additions & 0 deletions test/assert_json_has_only_test.rb
@@ -0,0 +1,94 @@
require_relative './test_helper'

class AssertJsonHasNoUnexpectedKeysTest < Minitest::Test
include AssertJson

def test_on_root_object
assert_json '{"keyA":"value","keyB":"value"}' do
has_only
has 'keyA'
has 'keyB'
end
end

def test_on_root_object_failure
err = assert_raises(MiniTest::Assertion) do
assert_json '{"keyA":"value","keyB":"value"}' do
has_only
has 'keyA', 'value'
end
end
assert_equal 'element root has unexpected keys: keyB', err.message
end

def test_on_root_object_with_sub_object
assert_json '{"keyA":{"subKeyA":"value","subKeyB":"value"},"keyB":"value"}' do
has_only
has 'keyA' do
has 'subKeyA'
has 'subKeyB'
end
has 'keyB'
end
end

def test_on_root_object_with_sub_object_failure
err = assert_raises(MiniTest::Assertion) do
assert_json '{"keyA":{"subKeyA":"value","subKeyB":"value"},"keyB":"value"}' do
has_only
has 'keyA' do
has 'subKeyA'
has 'subKeyB'
end
end
end
assert_equal 'element root has unexpected keys: keyB', err.message
end

def test_on_sub_object
assert_json '{"keyA":{"subKeyA":"value","subKeyB":"value"},"keyB":"value"}' do
has 'keyA' do
has_only
has 'subKeyA'
has 'subKeyB'
end
end
end

def test_on_sub_object_failure
err = assert_raises(MiniTest::Assertion) do
assert_json '{"keyA":{"subKeyA":"value","subKeyB":"value"},"keyB":"value"}' do
has 'keyA' do
has_only
has 'subKeyA'
end
end
end
assert_equal 'element keyA has unexpected keys: subKeyB', err.message
end

def test_on_root_array_of_objects
assert_json '[{"id":1, "key":"test", "name":"test"}, {"id":2, "key":"test", "name":"test"}, {"id":3, "key":"test", "name":"test"}]' do
has_only
item 0 do
has :id, 1
has :key, 'test'
has :name, 'test'
end
end
end

def test_on_root_array_of_objects_failure
err = assert_raises(MiniTest::Assertion) do
assert_json '[{"id":1, "key":"test", "name":"test"}, {"id":2, "key":"test", "name":"test"}, {"id":3, "key":"test", "name":"test"}]' do
has_only
item 0 do
has :id, 1
has :key, 'test'
end
end
end
assert_equal 'element 0 has unexpected keys: name', err.message
end

end

0 comments on commit b34011d

Please sign in to comment.