[rspec-users] Test JSON with Cucumber

101 views
Skip to first unread message

Bill Kocik

unread,
May 11, 2009, 3:59:32 PM5/11/09
to rspec-users
Hi Folks -

I'm hoping someone has come before me in trying to do this. I want to
use Cucumber to acceptance-test my JSON output. So far all I can do is
validate that the JSON is valid, with this step:

Then /^I should get valid JSON$/ do
assert_nothing_raised do
ActiveSupport::JSON.decode(@response.body)
end

What I'd really like to do is be able to drill down into the JSON
looking for the presence of certain elements in their correct places.
For example, if my JSON is {"foo":{"bar":"baz"}} and parses out to
{'foo' => {'bar' => 'baz'}} then I want to be able to test that
['foo']['bar'] gives me 'baz', or at least that ['foo']['bar'] exists.
Unfortunately I'm not sure how to go about writing step definitions to
do anything like this, since I haven't yet figured out a way to take a
regular expression or string and inspect a hash with it.

Anyone have any ideas? I could really use a brain-kick here.

Thanks...

--
Bill Kocik

http://bkocik.net
_______________________________________________
rspec-users mailing list
rspec...@rubyforge.org
http://rubyforge.org/mailman/listinfo/rspec-users

Ben Mabey

unread,
May 11, 2009, 4:32:22 PM5/11/09
to rspec-users
Bill Kocik wrote:
> Hi Folks -
>
> I'm hoping someone has come before me in trying to do this. I want to
> use Cucumber to acceptance-test my JSON output. So far all I can do is
> validate that the JSON is valid, with this step:
>
> Then /^I should get valid JSON$/ do
> assert_nothing_raised do
> ActiveSupport::JSON.decode(@response.body)
> end
>
> What I'd really like to do is be able to drill down into the JSON
> looking for the presence of certain elements in their correct places.
> For example, if my JSON is {"foo":{"bar":"baz"}} and parses out to
> {'foo' => {'bar' => 'baz'}} then I want to be able to test that
> ['foo']['bar'] gives me 'baz', or at least that ['foo']['bar'] exists.
> Unfortunately I'm not sure how to go about writing step definitions to
> do anything like this, since I haven't yet figured out a way to take a
> regular expression or string and inspect a hash with it.
>
> Anyone have any ideas? I could really use a brain-kick here.
>
> Thanks...
>
>

Well.. IIRC ActiveSupport::JSON.decode will return a ruby hash of the
JSON, correct? So you should be able to make expectations on it just
like a regular hash object.

json = ActiveSupport::JSON.decode(@response.body)
json['foo']['bar'].should == 'baz'.


Does that help or am I missing something about your question?

-Ben

Bill Kocik

unread,
May 11, 2009, 5:31:04 PM5/11/09
to rspec-users
On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <b...@benmabey.com> wrote:

> Well.. IIRC ActiveSupport::JSON.decode will return a ruby hash of the JSON,
> correct?  So you should be able to make expectations on it just like a
> regular hash object.

You're absolutely correct - I think I did a poor job of asking my
question. Maybe a more concrete example would help. Suppose I have
this JSON:

response:{code:'200',requestId:'1234'}

I want to have a scenario like this in my Cucumber feature file:

When I go to "the statuses JSON url"
Then I should find "....." # ['response']['code']
And I should find "....." #['response']['requestId']

With a step definition like:

Then /^I should find "([^\"]*)"$/ do |the_string|
json = ActiveSupport::JSON.decode(@response.body)
assert_not_nil ...... #(something with the json obect and the_string)
end

I don't know what goes in those blanks. Cucumber is going to give me a
string it found with the regex, but I don't know what the string would
look like in my "Then I should find" line in my feature file, nor what
to do with it in my assert line in the step definition. I'm not even
sure any of this is possible.

Consider if I were dealing with XML instead of JSON - I could put an
XPath expression in my "Then I should find" line, and in my step
definition I could search the XML with the given XPath expression
using hpricot or something. With JSON and it's resultant hash, I have
no idea where to start...


--
Bill Kocik

http://bkocik.net

Michael Schuerig

unread,
May 11, 2009, 6:03:54 PM5/11/09
to rspec...@rubyforge.org
On Monday 11 May 2009, Bill Kocik wrote:
> On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <b...@benmabey.com> wrote:
> > Well.. IIRC ActiveSupport::JSON.decode will return a ruby hash of
> > the JSON, correct? So you should be able to make expectations on
> > it just like a regular hash object.
>
> You're absolutely correct - I think I did a poor job of asking my
> question. Maybe a more concrete example would help. Suppose I have
> this JSON:
>
> response:{code:'200',requestId:'1234'}
>
> I want to have a scenario like this in my Cucumber feature file:
>
> When I go to "the statuses JSON url"
> Then I should find "....." # ['response']['code']
> And I should find "....." #['response']['requestId']
>
> With a step definition like:
>
> Then /^I should find "([^\"]*)"$/ do |the_string|
> json = ActiveSupport::JSON.decode(@response.body)
> assert_not_nil ...... #(something with the json obect and
> the_string) end

Do you want to cherry-pick individual pieces from the JSON, i.e. you
don't care about the exact response as long as the parts you're looking
for are there? Or would it be okay to check against a known good
response?

The latter case is easier. In the former case, how about a matcher that
checks the actual response is a superset of the expected response?

[...]


> Consider if I were dealing with XML instead of JSON - I could put an
> XPath expression in my "Then I should find" line, and in my step
> definition I could search the XML with the given XPath expression
> using hpricot or something. With JSON and it's resultant hash, I have
> no idea where to start...

There's JSONPath, but I don't think a Ruby implementation exists. If you
always have "definite" paths without wildcards and recursion(?) then you
might be able to do with some simple code like this

When I go to "the statuses JSON url"

Then I should find "response.code"

Then /^I should find "([^\"]*)"$/ do |the_string|
json = ActiveSupport::JSON.decode(@response.body)

obj = traverse(json, the_string)
assert_not_nil obj
end

def traverse(json, path)
path.split('.').inject(json) do |value, selector|
value = value[selector]
end
rescue
nil
end


Michael

--
Michael Schuerig
mailto:mic...@schuerig.de
http://www.schuerig.de/michael/

Zach Dennis

unread,
May 11, 2009, 6:42:24 PM5/11/09
to rspec-users

Another options is to call #to_xml on the JSON hash returned by
#decode, and then use XPath since you seem to already know it,

> --
> Bill Kocik
>
> http://bkocik.net
> _______________________________________________
> rspec-users mailing list
> rspec...@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>

--
Zach Dennis
http://www.continuousthinking.com (personal)
http://www.mutuallyhuman.com (hire me)
@zachdennis (twitter)

Bill Kocik

unread,
May 11, 2009, 7:40:09 PM5/11/09
to zach....@gmail.com, rspec-users
ooh - I love this solution. The downsides that immediately come to
mind are that I'd be putting XPath expressions in my acceptance tests
which aren't necessarily readable, but since I'm the only one who has
to read them that's alright, and that I have to parse JSON into a
hash, convert the hash to XML, and then parse that XML again with
Nokogiri or something, but assuming that the converters I'm using are
well-tested and predictable, that's an acceptable thing to me.

The upside is that I can query my JSON with arbitrary depth and
precision using XPath. Awesome.

I love having access to groups of people who are smarter than me.
Thanks for everyone's suggestions...

Hagar Chen

unread,
Jun 20, 2013, 6:48:05 AM6/20/13
to rspec...@rubyforge.org
Hi, I have a question about writing scenarios in cucumber that include a
JSON.
I have the following scenario in cucumber:

Scenario Outline:
Given I send and accept JSON
And I send a POST request to <path> with the following:
"""
{
"field1":"text",
"emails":[
{
"work":"te...@test.com",
"private":"ho...@test.com",
},
]
}
"""
Then The response status should be "200"

Examples:
| path |
| /req |

I want the JSON to be in the examples, so that I can run the scenario
several times with different JSONs and verify that I get the correct
response status.
I want the scenario to look something like this:

Scenario Outline:
Given I send and accept JSON
And I send a POST request to <path> with the following:
"""
<JSON>
"""
Then The response status should be "200"

how do I add the JSON to the examples?

--
Posted via http://www.ruby-forum.com/.
Reply all
Reply to author
Forward
0 new messages