Convert Test::Unit to RSpec by Script

Living with Test and Spec at the same time is annoying, so here is a small Howto for conversion using the Test::Unit to RSpec converter.

  1. Change spec/spec_helper.rg ‘config.fixture_path =‘ to test/fixtures OR copy all fixtures from test to spec (svn cp test/fixtures spec/fixtures)
  2. copy old code from test_helper to spec_helper (leave includes outside of Spec::Runner.configure do…)
  3. sudo gem install spec_converter
  4. convert all tests ‘spec_converter
  5. correct syntax errors
  6. search and replace ‘test/xxx’ with ‘spec/xxx’ where neccessary
  7. run ‘rake spec
  8. dance if result == :success

Works very nice for me, just one syntax error to correct, and then everything runs. Not all old asserts will be replaced, but this is not problem, since RSpec can work with Test::Unit assertions.

Instant Bug to Testcase

Today i found a small new plugin, that offers a simple way to convert bugs to a testcase.

script/plugin install http://svn.extendviget.com/lab/laziness/trunk

Now every failing request will print a small test case to repeat this request, it is not much, but it is a starting point and can be helpful if you have a lot of parameters being passed.

Output:
Laziness
def test_get_rating_edit_should_not_raise_activerecord_recordnotfound_exception
  assert_nothing_raised(ActiveRecord::RecordNotFound) do
    get :edit, {"id"=>"1"}, {:user_id=>nil, :return_to=>"/orders/1"}, {}, {"_session_id"=>["…"]}
  end
end
Patch:

Atm you need to apply this patch to make it work without exception_notification.

#vendor/plugins/laziness/lib/laziness.rb
module Laziness
  begin
    module ExceptionNotifier
      ::ExceptionNotifier.sections << 'laziness'
    end
  rescue
  end

Validate all Fixtures

UPDATE: Please have a look at valid_attributes

A enhancement to the validate all models task , adds a db:validate_fixtures, which basically loads the fixtures and then validates all models.


To make this work for RSpec you have to “ln -s PATH_TO/spec/fixtures test/fixtures”.

Output:

-- records - model --
         1x FtpAccount
         2x Movie
Movie: id=2
["Title can't be blank"]
         1x Order
         1x Rating
No Table for: Tableless
         4x User

Task:

#base: http://blog.hasmanythrough.com/2006/8/27/validate-all-your-records
#task: rake db:validate_models
namespace :db do
  def all_models
    #get all active record classes
    Dir.glob(RAILS_ROOT + '/app/models/**/*.rb').each do |file|
      begin
        require file unless file =~ /observer/
      rescue
        #require any possible file dependencies
        if $!.to_s =~ /^uninitialized constant (\w+)$/
          require $1.underscore + '.rb'
          retry
        else
          raise
        end
      end
    end
    klasses = Object.subclasses_of(ActiveRecord::Base)
    #throw out session if it is not stored in db
    klasses.reject! do |klass|
      klass == CGI::Session::ActiveRecordStore::Session && ActionController::Base.session_store.to_s !~ /ActiveRecordStore/
    end
    klasses.select{ |c| c.base_class == c}.sort_by(&:name)
  end

  def validate_models
    #validate them
    puts "-- records - model --"
    all_models.each do |klass|
      begin
        total = klass.count
      rescue
        #tableless, session...
        if $!.to_s =~ /Table .* doesn't exist/im
          puts "No Table for: #{klass}"
          next
        end
        raise
      end
      printf "%10dx %s\n", total, klass.name
      chunk_size = 1000
      (total / chunk_size + 1).times do |i|
        chunk = klass.find(:all, :offset => (i * chunk_size), :limit => chunk_size)
        chunk.reject(&:valid?).each do |record|
          puts "#{record.class}: id=#{record.id}"
          p record.errors.full_messages
          puts
        end rescue nil
      end
    end
  end

  desc "Run model validations on all model records in database"
  task :validate_models => :environment do
    validate_models
  end

  desc "Load and validate fixtures (and other models)"
  task :validate_fixtures do
    RAILS_ENV='test'
    
    Rake::Task[:environment].invoke
    Rake::Task["db:fixtures:load"].invoke
    
    validate_models
  end
end

Testing a single Example; Spec; Testcase; Test

UPDATE: everything is now on github

Testing a single spec, a single test, a single testcase or a single example is a great timesaver when debugging a small problem while having a large testsuite, or a testsuite with a lot of failures…

  • rake test:blog -> only the Blog Testcase
  • rake spec:blog -> only the Blog Spec
  • rake test:blog:create -> only the tests matching /create/ in Blog
  • rake spec:blog:delete -> only the first example matching /create/ in Blog
  • rake test:’admin/blogs_con’ -> only BlogsController Test in admin folder
  • rake test:xy -> first test matching xy*_test.rb (searched in order: unit,functional,integration,any folder)

It will search in unit/functional/integration (test) and models/controllers/views/helpers(spec).
Idea

Install:
UPDATE: everything is now on github

Intelligent Redirects: redirect_to_last_index

I often came across the same problem, the user starts say /users?order=email%20desc, chooses the user to edit and when he hits save/destroy he finds himself at /users/, very disturbing…. Another example comes from multiple controllers, when you start in /movies/3 and then, edit the user that created the movie, hit save and you resurface in /users/.

Fixing this redirecting issue was easier then i thought, and i refactored it to be a nice drop in and forget solution!

#app/application_controller.rb
...
  after_filter :save_last_index , :only => :index

protected
  def redirect_to_last_index
    return redirect_to session[:last_index] if session[:last_index]
    redirect_to :action => :index
  end

  def save_last_index
    session[:last_index]=request.request_uri
  end
...

#app/users_controller.rb
  def destroy
    @user = User.find(params[:id]).destroy
    redirect_to_last_index
  end

No more redirecting pain hurray 😀

Live example