article How to test your gem against multiple version of Rails?

19 Feb 2014
If you develop a gem which integrates with Rails you may want to test against multiple versions of it. My solution to this to use one page Rails applications with the appraisals gem and load the applications based on the actual gem version. Let me show you an example:
bundle gem example
      create  example/Gemfile
      create  example/Rakefile
      create  example/LICENSE.txt
      create  example/README.md
      create  example/.gitignore
      create  example/example.gemspec
      create  example/lib/example.rb
      create  example/lib/example/version.rb
Initializing git repo in ~/Github/example
We generated a gem skeleton. Let's prepare the gem for testing. We need to add some dependencies to the gemspec:
# example.gemspec
...
spec.add_development_dependency 'minitest', '>= 3'
spec.add_development_dependency 'appraisal'
...
Create the test folder with the test helper:
# test/test_helper.rb
require 'bundler/setup'
require 'minitest/autorun'
require 'example'

ENV["RAILS_ENV"] = "test"
ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'

require "apps/rails4"
Setup rake to run the tests:
require "bundler/gem_tasks"
require 'rake/testtask'
require 'appraisal'

Rake::TestTask.new do |t|
  t.libs = ["test"]
  t.pattern = "test/**/*_test.rb"

end
task :default => :test
Create an `Appraisal` file:
# Appraisals
appraise "rails-4" do
  gem "rails", "4.0.0"
  gem "sqlite3"
end
Than run `bundle install` and `rake appraisal:install` to generate the gemfile. The official Rails guide advises to use a `dummy` Rails app for testing but in their guide it is a structured setup and I think it is better to keep everything in one file in this scenario so we will create a one file Rails application:
# test/apps/rails4.rb
require "rails"
require 'rails/all'
require 'action_view/testing/resolvers'
require 'rails/test_help'

require 'example' # our gem

module Rails4
  class Application < Rails::Application
    config.root = File.expand_path("../../..", __FILE__)
    config.cache_classes = true

    config.eager_load = false
    config.serve_static_assets  = true
    config.static_cache_control = "public, max-age=3600"

    config.consider_all_requests_local       = true
    config.action_controller.perform_caching = false

    config.action_dispatch.show_exceptions = false

    config.action_controller.allow_forgery_protection = false

    config.active_support.deprecation = :stderr

    config.middleware.delete "Rack::Lock"
    config.middleware.delete "ActionDispatch::Flash"
    config.middleware.delete "ActionDispatch::BestStandardsSupport"
    config.secret_key_base = '49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk'
    routes.append do
      get "/" => "welcome#index"
    end
  end
end

class WelcomeController < ActionController::Base
  include Rails.application.routes.url_helpers
  layout 'application'
  self.view_paths = [ActionView::FixtureResolver.new(
    "layouts/application.html.erb" => '<%= yield %>',
    "welcome/index.html.erb"=> 'Hello from index.html.erb',
  )]

  def index
  end

end

Rails4::Application.initialize!
Than write an integration test which uses this controller:
# test/integration/welcome_controller_test.rb
require 'test_helper'

class WelcomeControllerTest < ActionController::TestCase

  test "should get index and our gem should do it's job" do
    get :index
    assert_response :success
    # do your gem specific assertion
  end
end
If we run `rake appraisal` now it will run this test and it will pass. Next step is to create a Rails 3 test app. First we need to setup the appraisal for that:
# Appraisals
...
appraise "rails-3_2" do
  gem "rails", "3.2.14"
  gem "sqlite3"
  gem "test-unit"
end
Run `rake appraisal:install`. We need to change our test helper to switch beetween the Rails apps based on the Rails version:
# test/test_helper.rb
require 'bundler/setup'
require 'minitest/autorun'
require 'example'

ENV["RAILS_ENV"] = "test"
ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:'
require "rails"
case Rails.version
when '3.2.14'
  require 'test/unit'
  require "apps/rails3_2"
when '4.0.0'
  require "apps/rails4"
end
Than we need to create the test application:
# test/apps/rails3_2.rb
require 'rails/all'
require 'action_view/testing/resolvers'
require 'rails/test_help'

require 'example' # our gem

class Rails3_2 < Rails::Application
  config.root = File.expand_path("../../..", __FILE__)
  config.cache_classes = true

  config.eager_load = false
  config.serve_static_assets  = true
  config.static_cache_control = "public, max-age=3600"

  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false

  config.action_dispatch.show_exceptions = false

  config.action_controller.allow_forgery_protection = false

  config.active_support.deprecation = :stderr

  config.middleware.delete "Rack::Lock"
  config.middleware.delete "ActionDispatch::Flash"
  config.middleware.delete "ActionDispatch::BestStandardsSupport"
  config.secret_token = "49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk"
  routes.append do
    get "/" => "welcome#index"
  end
end

class WelcomeController < ActionController::Base
  include Rails.application.routes.url_helpers
  layout 'application'
  self.view_paths = [ActionView::FixtureResolver.new(
    "layouts/application.html.erb" => '<%= yield %>',
    "welcome/index.html.erb"=> 'Hello from index.html.erb',
  )]

  def index
  end

end

Rails3_2.initialize!
Now if you ru `rake appraisal` your test suite will be run against both versions of Rails'. The repo for the source of this example: [multiple-rails-test-example](https://github.com/gregmolnar/multiple-rails-test-example)

Job listings

Post a Job!

Did you enjoy reading this? Follow me on Twitter or sign up to my newsletter for more content like this!

I run an indie startup providing vulnerability scanning for your Ruby on Rails app.

It is free to use at the moment, and I am grateful for any feedback about it.
If you would like to give it a spin, you can do it here: Vulnerability Scanning for your Ruby on Rails app!

Related posts