Forum: RSpec Test JSON with Cucumber

Ff168162d53e22788d576582b3527e97?d=identicon&s=25 Bill Kocik (Guest)
on 2009-05-11 22:02
(Received via mailing list)
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
C694a032be7518a0d704318895f8fe1d?d=identicon&s=25 Ben Mabey (mabes)
on 2009-05-11 22:40
(Received via mailing list)
Bill Kocik wrote:
>
>
> 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
Ff168162d53e22788d576582b3527e97?d=identicon&s=25 Bill Kocik (Guest)
on 2009-05-11 23:33
(Received via mailing list)
On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <ben@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
Bce1d1b7c3ec7b577dcb42e254899e6b?d=identicon&s=25 Michael Schuerig (Guest)
on 2009-05-12 00:12
(Received via mailing list)
On Monday 11 May 2009, Bill Kocik wrote:
>
>   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:michael@schuerig.de
http://www.schuerig.de/michael/
F86901feca747abbb5c6c020362ef2e7?d=identicon&s=25 Zach Dennis (zdennis)
on 2009-05-12 00:44
(Received via mailing list)
On Mon, May 11, 2009 at 5:31 PM, Bill Kocik <bkocik@gmail.com> wrote:
> response:{code:'200',requestId:'1234'}
>  json = ActiveSupport::JSON.decode(@response.body)
> 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...
>

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-users@rubyforge.org
> http://rubyforge.org/mailman/listinfo/rspec-users
>



--
Zach Dennis
http://www.continuousthinking.com (personal)
http://www.mutuallyhuman.com (hire me)
@zachdennis (twitter)
Ff168162d53e22788d576582b3527e97?d=identicon&s=25 Bill Kocik (Guest)
on 2009-05-12 01:42
(Received via mailing list)
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...


On Mon, May 11, 2009 at 6:42 PM, Zach Dennis <zach.dennis@gmail.com>
wrote:

> 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
5b1888f939fe27759759b4d14d932c36?d=identicon&s=25 Hagar Chen (hagar-chen)
on 2013-06-20 12:48
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":"test@test.com",
            "private":"home@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?
This topic is locked and can not be replied to.