简体   繁体   中英

Testing dynamically created methods

In my model, I dynamically create some methods based on database records:

class Job < ActiveRecord::Base
  belongs_to :job_status

  # Adds #requisition?, #open?, #paused?, #closed?
  class_eval do    
    JobStatus.all.each do |status|
      unless method_defined? "#{status.name.downcase}?"
        define_method("#{status.name.downcase}?") do 
          job_status_id == status.id
        end
      end
    end
  end
end

class JobStatus < ActiveRecord::Base
  has_many :jobs
end

The job_statuses table contains some seed data, so is not going to be frequently changing, but in case I ever need to add new statuses, I don't have to add more code to get a boolean method for the new status.

However, I am not sure how to test these methods, because when rspec starts the job_statuses table is obviously empty, and when the JobStatus objects are created, Job gets initialized, but since no objects exist yet, it doesn't create any methods, and my tests fail because the methods don't exist.

Note that I am using rspec with spork & guard, and using database-cleaner with the truncation strategy (as per Railscast #257 , since I'm using Selenium), so that probably complicates matters.

Try with enumerize gem. This make your status field like enumerator and build the "#{status.name.downcase}?" for your models. This gem came with it's own rspec-matchers making easiest your unit test.

The solution I came up with was to abstract the creation of runtime methods out into a library file, and then in my test file, remove and redeclare my class before each test, and reload the actual class (and blueprints) at the end of the suite:

describe AssociationPredicate do
  before(:all) do
    ["Continuous", "Standard"].each { |type| JobType.create!(:job_type => type) }
    ["Requisition", "Open", "Paused", "Closed"].each { |status| JobStatus.create!(:job_status => status) }
  end

  after(:all) do
    DatabaseCleaner.clean_with :truncation, :only => %w( job_types job_statuses )

    # Reload Job model to remove changes
    Object.send(:remove_const, 'Job')
    load 'job.rb'
    load 'support/blueprints.rb'
  end

  before(:each) do
    Object.send(:remove_const, 'Job')

    # Redefine Job model for testing purposes
    class Job < ActiveRecord::Base
      belongs_to  :job_type
      belongs_to  :job_status

      has_many    :job_applications
    end
  end

  it "should add methods when included" do
    Job.send(:association_predicate, :job_type)
    job.should respond_to(:continuous?)
    job.should respond_to(:standard?)
  end
end

This way, I create a basic class for each test, add the runtime methods as necessarily, and return to the actual class when I'm done.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM