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

Banner image