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. 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, form actions) still include the mount point, so a test that follows a redirect needs Padrino.application.

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, with any test framework, including RSpec):

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 it only adds the gem and prints how to set it up.

Type to search the guides.