Ruby on rails 如何使用Rspec测试rubyonrails中的链式方法

Ruby on rails 如何使用Rspec测试rubyonrails中的链式方法,ruby-on-rails,testing,rspec,Ruby On Rails,Testing,Rspec,我正试图决定如何测试一种方法,该方法只计算关联记录上的平均值。我关心的是测试实现与返回的实际结果 假设我有以下型号 class User has_many :interviews def interview_grade interviews.average(:score).round unless interviews.empty? end end class Interview belongs_to :user end 在user\u spec.rb中,我有 de

我正试图决定如何测试一种方法,该方法只计算关联记录上的平均值。我关心的是测试实现与返回的实际结果

假设我有以下型号

class User
  has_many :interviews

  def interview_grade
    interviews.average(:score).round unless interviews.empty?
  end
end

class Interview
  belongs_to :user
end
user\u spec.rb
中,我有

describe "interview_grade" do
  let(:user) {User.new}
  context "when the user has interviews" do
    before { user.stub_chain(:interviews, :empty?){false} }
    it "should return an average of the appraisal ratings" do
      user.interviews.should_receive(:average).with(:score).and_return(3.2)
      user.work_history_grade.should == 3
    end
  end

  context "when the user has no interviews" do
    before {Interview.destroy_all}
    it "should return nil" do
      user.interview_grade.should be_nil
    end
  end

end
这些测试都通过了,但我觉得很脆弱。如果
interview\u grade
应该实际计算分数的总和(例如)。因为我只是测试调用了一个特定的方法链,所以通过测试不会告诉我结果实际上是不正确的

我已经尝试了stubing
user.conferences
,以便为测试设置可用的分数,但是在Rails 3中这似乎很难做到,因为关联是惰性加载的。i、 e.我不能仅仅创建一个访谈对象数组,因为它不响应平均
方法


非常感谢您的建议。

这是存根和模仿的错误用法

在这种情况下,您应该只测试
interview\u grade
是否有效,当
average
返回nil时(这是唯一的案例
interview.empty?

average
方法由rails本身进行测试<我猜是ruby测试的code>round
方法。所以你不需要测试这个方法。这是只测试您自己的代码的一般想法

如果你想测试面试分数是如何计算的,你应该创建测试数据(用夹具或工厂)。因为在某些情况下,您应该单独测试系统的一部分,而在这种情况下,分离是错误的:interviews.average和interviews.empty?它们在代码中是依赖的,但在规范中是独立的

def面试等级
面试。平均(:分数)。尝试(:轮)
结束

如果您以这种方式重写您的方法,那么您就不需要在3年后再回到这个问题上来了。我会采取完全不同的做法

下面代码的好处是,为了给InterviewGrader编写测试,我不再需要担心如何获得分数

我只是给它分数,然后测试它给了我正确的输出

此外,我永远不需要担心InterviewGrader的底层实现。但是,如果以后更改逻辑,测试将失败

需要单独测试
用户
上的新
评分
方法

class InterviewGrader

  def self.run scores
    new(scores).run
  end

  attr_reader :scores

  def initialize(scores)
    @scores = scores
  end

  def run
    scores.inject { |sum, score|
      sum + score
    }.to_f / number_of_scores
  end

  private

  def number_of_scores
    scores.length
  end

end


class User
  has_many :interviews

  def scores
    interviews.map(&:score)
  end

  def interview_grade
    InterviewGrader.run(scores)
  end
end

class Interview
  belongs_to :user
end

我更喜欢在这里使用try,我没有想到这一点。然而,我尽量避免使用工厂,因为它们速度慢。我更喜欢模拟user.interfaces,但正如我在问题中提到的,由于延迟加载关联,我还没有在Rails3中找到一种很好的方法来实现这一点。