繁体   English   中英

如何使用 Rspec 在 Rails 上测试 Ruby 中的链式方法

[英]How to test chained methods in Ruby on Rails using 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_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_grade应该实际计算分数的总和(例如)怎么办。 因为我只是在测试是否调用了特定的方法链,所以这个通过测试不会告诉我结果实际上是不正确的。

我已经尝试对user.interviews进行存根,以便为测试设置可用的分数,但这在 Rails 3 中似乎很棘手,因为关联是延迟加载的。 即我不能只创建一个采访对象数组,因为它不响应平均method

非常感谢任何建议。

3年后回到这个话题。 我会以完全不同的方式处理它。

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

我只是给它打分并测试它给了我正确的 output。

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

User上的新scores方法需要单独测试。

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

这是存根和 mocking 的错误使用。

在这种情况下,您应该只测试interview_grade是否有效,当average返回 nil 时(这只是使用了interviews.empty?的案例)。

average方法是由rails本身测试的。 ruby 测试的round方法(我猜)。 所以你不需要测试这个方法。 这是仅测试您自己的代码的一般想法。

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

def interview_grade
  interviews.average(:score).try(:round)
end

如果您以这种方式重写您的方法,则无需存根和 mocking

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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