我正在尝试决定如何测试一个简单计算相关记录值的平均值的方法。我担心测试实现与返回的实际结果。
说我有以下型号......
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中显得很棘手。即我不能只创建一个Interview对象数组,因为它不响应平均method
。
任何建议都非常感谢。
答案 0 :(得分:1)
3年后再回到这里。我会以完全不同的方式处理它。
以下代码的好处是,为了为InterviewGrader编写测试,我不再需要担心如何获得分数。
我只是给它分数并测试它给我正确的输出。
此外,我永远不必担心InterviewGrader的底层实现。但是,如果逻辑在以后更改,测试将失败。
scores
上的新User
方法需要单独测试。
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
答案 1 :(得分:0)
这是对存根和模拟的错误使用。
在这种情况下,您只应测试interview_grade
何时有效average
返回nil(这只是interviews.empty?
使用的情况)。
average
方法由rails本身测试。通过ruby测试的round
方法(我猜)。所以你不需要测试这种方法。这是一个通用的想法,只测试你自己的代码。
如果你想测试,如何计算interview_grade,你应该创建测试数据(使用fixtures或工厂)。因为你应该单独测试(在某些情况下)系统的一部分,在这种情况下,分离是错误的:interview.average和interview.empty?依赖于您的代码,但在规范中它们是独立的。
def interview_grade
interviews.average(:score).try(:round)
end
如果以这种方式重写方法,则无需使用存根和模拟