2012-03-12 97 views
1

在我的模型,我动态地创建一些方法基于数​​据库记录:测试动态创建的方法

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 

job_statuses表包含了一些种子数据,所以不会经常发生变化,但万一我曾经需要添加新的状态,我不需要添加更多的代码来获取新状态的布尔方法。

但是,我不知道如何测试这些方法,因为当rspec的开始job_statuses表显然是空的,并创建JobStatus对象时,Job被初始化,但由于还没有对象存在的,它不创建任何方法,并且我的测试失败,因为方法不存在。

请注意,我正在使用rspec和spork &后卫,并使用数据库清理程序和截断策略(根据Railscast #257,因为我使用的是Selenium),所以可能会使事情复杂化。

回答

0

我想出的解决方案是将创建的运行时方法抽象为一个库文件,然后在我的测试文件中,在每次测试之前删除并重新声明我的类,然后将实际的类(和蓝图)重新加载到该套件的结尾:

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 

这样,我创建了一个基本类为每个测试,添加运行方式为必然,并返回到实际上课的时候,我做了。

0

试用enumerize宝石。这使您的状态字段像枚举器一样,并构建“#{status.name.downcase}?”为您的模型。 This gem附带它自己的rspec-matchers,使您的单元测试更容易。