Blog ยป Environments Testing
Posted on 07 Sep 2010 11:57
A few words about testing related gems, caching and authenticated testing. Bootstrap advice for all your projects.
Gemfile
Use bundler and have this section in your $project/Gemfile
group :test do gem "factory_girl" gem "shoulda" gem "mocha" gem "rspec", "= 1.3.1", :require => nil gem "rspec-rails", "= 1.3.3", :require => nil gem "steak" gem "capybara" end gem 'ruby-debug', :groups => [:test, :development]
If you are using bundler then config.gem entries in test.rb are redundant.
config/environments/test.rb
Set caching to true in test environment. Otherwise you're not testing what will happen in the production environment.
Also configure testing only gems here. Here is my config/environments/test.rb file:
# Settings specified here will take precedence over those in config/environment.rb # The test environment is used exclusively to run your application's # test suite. You never need to work with it otherwise. Remember that # your test database is "scratch space" for the test suite and is wiped # and recreated between test runs. Don't rely on the data there! config.cache_classes = true # Log error messages when you accidentally call methods on nil. config.whiny_nils = true # Show full error reports and disable caching config.action_controller.consider_all_requests_local = true config.action_controller.perform_caching = true config.action_view.cache_template_loading = true # Disable request forgery protection in test environment config.action_controller.allow_forgery_protection = false # Tell Action Mailer not to deliver emails to the real world. # The :test delivery method accumulates sent emails in the # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test # Use SQL instead of Active Record's schema dumper when creating the test database. # This is necessary if your schema can't be completely dumped by the schema dumper, # like if you have constraints or database-specific column types # config.active_record.schema_format = :sql config.action_controller.perform_caching = true #otherwise caching errors are hidden from tests - umur config.i18n.default_locale = :en config.gem 'thoughtbot-factory_girl', :lib => 'factory_girl', :source => 'http://gems.github.com' config.gem 'thoughtbot-shoulda', :lib => 'shoulda', :source => 'http://gems.github.com' config.gem 'rack-test', :lib => 'rack/test' config.gem 'mocha' #config.gem 'webrat' config.gem 'rspec', :lib=>false, :version => '1.3.1' config.gem 'rspec-rails', :lib=>false, :version => '1.3.3' config.gem 'steak' config.gem 'capybara' #config.gem 'rack-cache'
At least I am using those gems:
- Factory Girl
- Shoulda
- Mocha
- Webrat
- Rack Test. Actually using rack test is not necessary if you are using Webrat. They are alternatives.
Caching is enabled all the way. This way you know tests are passing while you are using the caches!!!
Locale is set to :en (English) intentionally. This way you can have a multi lingual application. But you are validating the application against the English version… Otherwise it's really a pain to test the interface of a multi lingual application.
tests/test_helper.rb
Now in your tests/test_helper.rb,,
- you need to initialize WebRat
- turn off fixtures if you are using factory girl
- have a login_as(user) helper. You want to test authenticated methods right? You need to adapt the method according to the authentication you're using
this is my tests/test_helper.rb
ENV["RAILS_ENV"] = "test" require File.expand_path(File.dirname(__FILE__) + "/../config/environment") require 'test_help' Webrat.configure do |config| config.mode = :rails #http://gitrdoc.com/rdoc/brynary/webrat/273e8c541a82ddacf91f4f68ab6166c16ffdc9c5/classes/Webrat/Configuration.html end class ActiveSupport::TestCase # Transactional fixtures accelerate your tests by wrapping each test method # in a transaction that's rolled back on completion. This ensures that the # test database remains unchanged so your fixtures don't have to be reloaded # between every test method. Fewer database queries means faster tests. # # Read Mike Clark's excellent walkthrough at # http://clarkware.com/cgi/blosxom/2005/10/24#Rails10FastTesting # # Every Active Record database supports transactions except MyISAM tables # in MySQL. Turn off transactional fixtures in this case; however, if you # don't care one way or the other, switching from MyISAM to InnoDB tables # is recommended. # # The only drawback to using transactional fixtures is when you actually # need to test transactions. Since your test is bracketed by a transaction, # any transactions started in your code will be automatically rolled back. self.use_transactional_fixtures = false # Instantiated fixtures are slow, but give you @david where otherwise you # would need people(:david). If you don't want to migrate your existing # test cases which use the @david style and don't mind the speed hit (each # instantiated fixtures translates to a database query per test method), # then set this back to true. self.use_instantiated_fixtures = false # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order. # # Note: You'll currently still have to declare fixtures explicitly in integration tests # -- they do not yet inherit this setting # fixtures :all # Add more helper methods to be used by all tests here... class << self def could do_sth puts "Could #{do_sth}" end alias feature context end def cleanup ActiveRecord::Base.delete_everything! end # def setup; super; cleanup; end def teardown; cleanup; super; end end class ActionController::TestCase #http://alexbrie.net/1526/functional-tests-with-login-in-rails/ # def login_as(user) # @request.session[:user] = user ? user.id : nil # end #http://alexbrie.net/1526/functional-tests-with-login-in-rails/ def login_as(user) old_controller = @controller @controller = UsersController.new post :login, :login=>user.email_address, :password=>user.password # assert_redirected_to :controller => tsap, :action=>'overview' # session[:user] = user.typed_id assert_not_nil(session[:user]) @controller = old_controller end end module Rack module Utils class HeaderHash puts "Rack::Utils::HeaderHash bug fix on #replace" def replace other self.clear other.each { |k,v| self[k] = v } end end end end
Note that use_transactional_fixtures is false. And there is another method to clean up the data. This requires you to follow deleting-all-records-in-rails. Then you will have "ActiveRecord::Base.delete_everything!" defined in your project.
Without such a setting, you cannot test explicit transactions in your code.
There is a fix on class HeaderHash. Without this you cannot rack test, the metal in your application. If this statement is meaningless for you then don't bother. But you might come across the problem in the future.
config/environment.rb
Configure Rack::Cache in environment.rb. This way it will work in development and test but it wont work in production. I am deploying to Heroku. Heroku provides Varnish. Rack::Cache in production is redundant against Varnish and makes you pay a fat bill to Heroku. Include the "unless" block in the config block
# unless "RAILS_ENV" == "production" # require 'rack/cache' # config.gem 'rack-cache' # config.middleware.use Rack::Cache, # :verbose => true, # :metastore => 'file:/tmp/cache/rack/meta', # :entitystore => 'file:/tmp/cache/rack/body' # # I am deploying to Heroku # # Heroku has Varnish which replaces rack-cache # # The purpose is to have caching in test and development # end
Warning The code above is optional! Use it if and only if you have experience on html page caching. Otherwise you might loose your ability to see updated pages. That's why I kept the example code commented out.
I also keep memcached configuration (Northscale Memcached) in environments.rb. Because I use it both in production and test:
require 'memcached' config.cache_store = :mem_cache_store, Memcached::Rails.new
Note that everybit has to go into Rails::Initializer block in config/environment.rb
spec/spec_helper.rb
I am writing Acceptance Tests using Steak/Capybara.
First initialize the steak environment as described in steak-capybara-on-rails-2-3 executing this bit:
script/generate rspec #Initialize rspec script/generate steak --capybara #Initialize steak
Then modify the file spec/spec_helper.
Similar to the configuration above, you are disabling transactional fixtures:
- They don't work with Capybara, anyway
- You cannot test transactions in your application
# This file is copied to ~/spec when you run 'ruby script/generate rspec' # from the project root directory. ENV["RAILS_ENV"] ||= 'test' require File.expand_path(File.join(File.dirname(__FILE__),'..','config','environment')) require 'spec/autorun' require 'spec/rails' # Uncomment the next line to use webrat's matchers #require 'webrat/integrations/rspec-rails' # Requires supporting files with custom matchers and macros, etc, # in ./support/ and its subdirectories. Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f} Spec::Runner.configure do |config| # If you're not using ActiveRecord you should remove these # lines, delete config/database.yml and disable :active_record # in your config/boot.rb config.use_transactional_fixtures = false config.use_instantiated_fixtures = false config.fixture_path = RAILS_ROOT + '/spec/fixtures/' # == Fixtures # # You can declare fixtures for each example_group like this: # describe "...." do # fixtures :table_a, :table_b # # Alternatively, if you prefer to declare them only once, you can # do so right here. Just uncomment the next line and replace the fixture # names with your fixtures. # # config.global_fixtures = :table_a, :table_b # # If you declare global fixtures, be aware that they will be declared # for all of your examples, even those that don't use them. # # You can also declare which fixtures to use (for example fixtures for test/fixtures): # # config.fixture_path = RAILS_ROOT + '/spec/fixtures/' # # == Mock Framework # # RSpec uses its own mocking framework by default. If you prefer to # use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr # # == Notes # # For more information take a look at Spec::Runner::Configuration and Spec::Runner #add information to log about what spec is currently run config.before(:each) do full_example_description = "Starting #{@method_name}" Rails::logger.info("\n\n#{full_example_description}\n#{'-' * (full_example_description.length)}") end config.after(:each) do Rails::logger.info('clean up data') ActiveRecord::Base.delete_everything! end
spec/acceptance/support/helpers.rb
If you are using steak tests then you need to have a way to log in during the test. So provide logger and login_as methods in addition to other things you might have:
#spec/acceptance/support/helpers.rb module HelperMethods # Put here any helper method you need to be available in all your acceptance tests def logger Rails::logger end def login_as(user) visit '/login' fill_in 'login', :with => user.email_address fill_in 'password', :with => user.password click_button 'Log in' end end Spec::Runner.configuration.include(HelperMethods)
If you like this page, please spread the word:
You can contact me if you have questions or corrections.