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
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
On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <ben at 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
On Monday 11 May 2009, Bill Kocik wrote:> On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <ben at 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) endDo 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 at schuerig.de http://www.schuerig.de/michael/
On Mon, May 11, 2009 at 5:31 PM, Bill Kocik <bkocik at gmail.com> wrote:> On Mon, May 11, 2009 at 4:32 PM, Ben Mabey <ben at 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... >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 at rubyforge.org > http://rubyforge.org/mailman/listinfo/rspec-users >-- Zach Dennis http://www.continuousthinking.com (personal) http://www.mutuallyhuman.com (hire me) @zachdennis (twitter)
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 at 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