Testing a web app
rack-test
A web test does not need a server, a port or a socket. Rack apps are objects with call(env), so a test can build the environment and call the app directly; rack-test wraps that in get, post and last_response:
gem install rack-test
1 gem installed
The test runs in milliseconds, the same speed as a unit test, and it exercises the routing, the middleware and the responses exactly as the server would.
The app
The app is the Sinatra class from lesson 137, in its own file so the test can require it without starting a server:
require "sinatra/base"
require "json"
class WebApp < Sinatra::Base
get "/health" do
content_type :json
{ status: "ok" }.to_json
end
post "/echo" do
content_type :json
{ received: params[:name] }.to_json
end
end
The test
include Rack::Test::Methods adds the request methods, and app returns the Rack app under test:
require "minitest/autorun"
require "rack/test"
require "json"
require_relative "../app/web_app"
WebApp.set :host_authorization, { permitted_hosts: [] }
class AppTest < Minitest::Test
include Rack::Test::Methods
def app = WebApp
def test_health
get "/health"
assert last_response.ok?
assert_equal "ok", JSON.parse(last_response.body)["status"]
end
def test_echo
post "/echo", name: "Ada"
assert_equal 200, last_response.status
assert_equal "Ada", JSON.parse(last_response.body)["received"]
end
end
The host_authorization line is Sinatra 4's host check: rack-test sends Host: example.org, and the protection answers 403 before the route runs. The first run of this test failed with exactly that, which is why the line is here; a real deployment keeps a list of allowed hosts instead of an empty one.
Running it
ruby test/app_test.rb
.. Finished in 0.012782s, 156.476 runs/s, 312.952 assertions/s. 2 runs, 4 assertions, 0 failures, 0 errors, 0 skips
Two assertions per request: the status and one field of the body. That is the standard shape of a web test, and it catches the two failures that matter — a route that moved and a JSON shape that changed.
What to watch for
A test that parses JSON before checking the status hides the real error:
a 500 HTML page becomes a
JSON::ParserError. Assert
the status first, then the body.
rack-test exercises the app, not the server. A bug in the Puma
configuration, a bad TLS certificate or a missing static file only
appears in an end-to-end test against a running process.
Next: Minitest in spec style
Article author: Arthur Isaev