Expanding from my previous post on a Grape API mounted on RACK.
Refactoring the Application Instance
Instead of sticking all of the Rack application code into config.ru, lets build a cleaner Acme::App _(in app/acme_app.rb). We’re going to drop _Rack::TryStatic and build this logic ourselves, since we might need to deal with other error codes than 404 (depending on your URL strategy you may be tripping over a 405). The logic remains the same: we try a bunch of static files and delegate to the API otherwise. You can also build primitive routing instead, so that everything requesting /api goes to the API and everything else goes to Rack::Static. Your mileage will vary.
module Acme
class App
def initialize
@filenames = ['', '.html', 'index.html', '/index.html']
@rack_static = ::Rack::Static.new(
lambda { [404, {}, []] }, {
:root => File.expand_path('../../public', __FILE__),
:urls => %w[/]
})
end
def call(env)
request_path = env['PATH_INFO']
# static files
@filenames.each do |path|
response = @rack_static.call(env.merge({'PATH_INFO' => request_path + path}))
return response if response[0] != 404
end
# api
Acme::API.call(env)
end
end
end
RSpec API Tests
Now that we have an application class, we can add API and Capybara integration tests. We start with RSpec and Rack test gems in Gemfile.
group :test do
gem "rspec"
gem "rack-test"
gem "rspec-core"
gem "rspec-expectations"
gem "rspec-mocks"
end
The spec/spec_helper.rb adds Rack::Test.
require 'rubygems'
ENV["RACK_ENV"] ||= 'test'
require 'rack/test'
require File.expand_path("../../config/environment", __FILE__)
RSpec.configure do |config|
config.mock_with :rspec
config.expect_with :rspec
end
Testing an API involves making requests on the Rack application, pretty straightforward.
require 'spec_helper'
describe Acme::API do
include Rack::Test::Methods
def app
Acme::API
end
context "v1" do
context "system" do
it "ping" do
get "/api/v1/system/ping"
last_response.body.should == { :ping => "pong" }.to_json
end
end
end
end
RSpec Capybara Integration Tests
Notice that in the tests above we’re mounting the Rack application and making requests directly to it. Does it actually work in a browser? Do we see the public/index.html page?
We start by adding capybara into Gemfile. At the time of the writing we need to use the code from Capybara head, since it adds support for Capybara.app.
group :test do
gem "capybara", :git => "https://github.com/jnicklas/capybara.git"
end
The spec/spec_helper.rb requires capybara/rspec, which brings in methods like page.visit and assigns an instance of the application to Capybara.app. Capybara will launch the application for us.
require 'capybara/rspec'
Capybara.configure do |config|
config.app = Acme::App.new
end
An integration test can go into spec/integration and must be marked with request: true _and _js: true (the latter forces the use of the Selenium driver that will popup a browser). Let’s look for a proper title on the homepage.
require 'spec_helper'
describe "Grape on RACK", :js => true, :type => :request do
context "homepage" do
before :each do
visit "/"
end
it "displays index.html page" do
page.find("title").text.should == "Rack Powers Web APIs"
end
end
end
A POST, PUT and Some JQuery
The sample source in https://github.com/dblock/grape-on-rack also adds JQuery, extends the API to simulate a persisted counter, and makes PUT requests to it. Complete with an integration test. Run bundle install and bundle exec rackup _to see it and _bundle exec rspec spec to run the tests.
Backbone.js w/ Mongo
@knewter put together a neat Backbone.js + MongoDB w/ Mongoid demo using Grape that is built in a similar manner, https://github.com/knewter/grape_demo.