Testing

Padrino apps are Rack apps, so you test them with rack-test: send a request, then check the response. Choose a test framework, and optionally a mocking library, when you generate the project:

$ padrino g project demo -t rspec
$ padrino g project demo -t minitest -m mocha

The project generator lists every test and mock component. The generated project has a test helper and a rake task: RSpec uses spec/spec_helper.rb; minitest, test-unit, shoulda and bacon use test/test_config.rb; cucumber uses features/support/env.rb plus RSpec's spec/spec_helper.rb. The controller, model and helper generators add a test file for what they create.

Run the suite, or a single file:

$ bundle exec rake spec    # RSpec
$ bundle exec rake test    # minitest, test-unit, shoulda and bacon
$ bundle exec rspec spec/app/controllers/posts_controller_spec.rb
$ bundle exec ruby -Itest test/app/controllers/posts_controller_test.rb

Requests

The test helper includes Rack::Test::Methods, so a test can call get, post, put, delete and the other verbs, then read last_response:

# spec/app/controllers/posts_controller_spec.rb
require 'spec_helper'

RSpec.describe '/posts' do
  it 'lists the posts' do
    get '/posts'
    expect(last_response).to be_ok
    expect(last_response.body).to eq 'Hello World'
  end

  it 'creates a post' do
    post '/posts/create', title: 'Hi'
    expect(last_response.body).to eq 'created Hi'
  end
end

The same test with minitest:

# test/app/controllers/posts_controller_test.rb
require File.expand_path("#{__dir__}/../../test_config.rb")

describe '/posts' do
  it 'lists the posts' do
    get '/posts'
    assert last_response.ok?
    assert_equal 'Hello World', last_response.body
  end
end

Requests go to the app method defined in the test helper. By default it returns Padrino.application, which serves every mounted app at its mount point, so a request to an app mounted at /blog starts with /blog. To test one app on its own, return that app class instead (shown here with minitest). Request paths then don't include the mount point:

describe 'the blog' do
  def app
    Demo::Blog
  end

  it 'lists the posts' do
    get '/posts'
    assert last_response.ok?
  end
end

URLs the app generates (url, redirect url(...), form actions) include the mount point only once Padrino.application has been built in the test process, since that is what gives each app its mount point. When you test an app on its own, call Padrino.application once in the test helper so the URLs are the same whichever tests run first.

The test environment

The test helper defines RACK_ENV = 'test' before it loads config/boot.rb. The generated config/apps.rb turns CSRF protection off in that environment:

set :protect_from_csrf, Padrino.env != :test

So a test can post without an authenticity_token. To test the protection itself, see CSRF Protection.

Sessions

rack-test keeps cookies between the requests of one test, so a session started by one request (a login, for example) is there for the next. To start a test with values already in the session, pass them in the rack.session entry of the request's Rack env, the third argument:

it 'shows the posts of the signed-in user' do
  get '/posts/mine', {}, 'rack.session' => { user_id: 1 }
  expect(last_response.body).to eq 'posts of user 1'
end

To check what a request stored in the session, read it from last_request:

post '/posts/create', title: 'Hi'
expect(last_request.env['rack.session'][:last_title]).to eq 'Hi'

Stubbing helpers

Helpers are instance methods of the app, and each request runs on a new instance of the app class. To stub a helper such as current_user in a controller test, stub it on any instance of the app class that handles the request.

With RSpec's own mocks (a project generated without -m):

it 'shows the posts of the current user' do
  allow_any_instance_of(Demo::App).to receive(:current_user).and_return('alice')
  get '/posts/mine'
  expect(last_response.body).to eq 'posts of alice'
end

With mocha (-m mocha), shown here with minitest. In an RSpec project, write the check as expect(last_response.body).to eq 'posts of alice':

it 'shows the posts of the current user' do
  Demo::App.any_instance.stubs(:current_user).returns('alice')
  get '/posts/mine'
  assert_equal 'posts of alice', last_response.body
end

Both remove the stub when the test ends. The generator sets mocha up for RSpec, minitest, test-unit and shoulda; with bacon or cucumber it only adds the gem and prints how to set it up.

Type to search the guides.