I suspected/ hoped that there must be a way to test outputs in JSON using Cucumber and Ruby without having to convert the JSON into XML and then parsing it using XPath. Seemed like an awful lot of effort to do something that feels like it should be quite simple.
I have managed to find what I believe to be a better way of doing this using the jsonpath gem.
I've been particualrly interested in this as a way to get automated tests described in Cucumber to be able make changes, perhaps at the GUI level, and then check that the JSON outputs are correct. I've basically been looking for a way to create acceptance tests that can test either the GUI, the JSON APIs or both of these for non-Ruby-on-Rails applications.
For example, assumming I wanted to test that the Twitter JSON API was updating with my most recent tweet, then I might start with a feature like:
Feature: JSON API updates with my Twitter Status
In order to be able to check my Twitter status
As a Twitter JSON API user
I want to be able to find my most recent status message
Scenario: Check my updated status on Twitter JSON API
Given that I have a Twitter account
And I update my Twitter status
When I query the Twitter JSON API
Then I should be able to find my most recent status update
Then the following is an example of what my step definitions using JSONPath might look like. Assuming that Ruby, Cucumber and Rspec already installed, the following gems would be needed to get this to work -
gem install json
gem install jsonpath
As I'm using watir-webdriver to drive the browser, then I would also need to install (these are purely for the browser tests and nothing to do with the JSON):
gem install selenium-webdriver
gem install watir-webdriver
Then the code to update my Twitter status through the browser and then check that this is updated could look something like (excepting a couple of annotations in the notes):
require 'rubygems'
require 'json'
require 'net/http'
require 'spec'
require 'jsonpath'
require 'watir-webdriver'
def twitter_json_api(twitter_name, count)
base_url = "http://api.twitter.com"
url = "#{base_url}/status/user_timeline/#{twitter_name}.json?count=#{count}"
response = Net::HTTP.get_response(URI.parse(url))
data = response.body
# Potential gotcha next. Much of the advice for the json
# gem says that it returns the JSON in a hash
# but when the JSON is an array, it is returned
# as an array and not a hash. You just need
# to know which you will be dealing with.
@result = JSON.parse(data)
return @result
end
Given /^that I have a Twitter account$/ do
@twitter_name = "a_valid_twitter_name"
@twitter_password = "a_valid_password"
end
Given /^I update my Twitter status$/ do
browser = Watir::Browser.new(:firefox)
browser.goto("http://www.twitter.com")
browser.span(:xpath, "//span[.='Sign in']").click
browser.text_field(:id, "username").set(@twitter_name)
browser.text_field(:id, "password").set(@twitter_password)
browser.button(:id, "signin_submit").click
@content = "Tweet tweet tweety tweet twoo"
browser.text_field(:id, "status").set(@content)
browser.link(:id, "tweeting_button").click
sleep(20)
#This is potential weakness that is worth consideration.
#The Twitter API doesn't update as fast as the script runs.
end
When /^I query the Twitter JSON API$/ do
#Passing a count of 1 to the API ensures I only get the most recent tweet.
@count = "1"
twitter_json_api(@twitter_name, @count)
end
Then /^I should be able to find my current status$/ do
JsonPath.new('$.text').on(@result[0]).to_a.should== @content
end
I thought this might of use/ interest to others so I dusted off my blogging shoes.
References:
Yahoo Developer Network: Parse JSON using Ruby
http://goessner.net/articles/JsonPath/
joshbuddy's jsonpath