3

我正在尝试为以下方法编写测试:

def average_rating
  reviews = self.reviews
  review_sum = reviews.inject(0) { |sum, review| sum += review.rating }
  avg_rating = (review_sum / reviews.count).to_i unless review_sum == 0
end

这是我到目前为止所拥有的:

describe "#average_rating" do
it "should be nil when there are no reviews" do
  api_without_reviews = Api.new name: 'Fake API', description: 'It does something', category: 'Fake category', url: 'http://www.example.com'
  api_without_reviews.average_rating.should be_nil
end
it "should calculate average rating properly" do
  api_with_reviews = Api.new name: 'Fake API', description: 'It does something', category: 'Fake category', url: 'http://www.example.com'
  api_with_reviews.save
  api_with_reviews.reviews.create rating: Random.rand(1..5), thoughts: 'blah'
  api_with_reviews.reviews.create rating: Random.rand(1..5), thoughts: 'blah'
  average = api_with_reviews.reviews.inject(0) { |sum, review| sum += review.rating } / api_with_reviews.reviews.count
  api_with_reviews.average_rating.should eq average
end

结尾

如何测试 review_sum 变量是否正确计算总和,或者该方法是否已经过全面测试?我是 rspec 的新手,因此感谢您的帮助!

4

3 回答 3

2

要直接回答,我建议您不要:

  • 生成评论评分的随机值
  • 直接复制方法代码进行验证。你不能给自己的论文评分,不是吗?

反而:

  • 使用手动创建的评分并手动计算平均值
  • 验证该方法是否可以输出您的计算结果。

进一步改进

  • 使用 FactoryGirl 创建测试对象。
  • 干燥代码

我的版本(假设安装了 FactoryGirl)

describe "API" do
  describe "average_rating" do
    # Use "before" but not "let" because this var must be clean in every example
    before { @api = FactoryGirl.create(:api) }
  
    it "should be nil when there are no reviews" do
      @api.average_rating.should be_nil
    end

    it "should calculate average rating properly" do
      [1, 5].each do |r|
        FactoryGirl.create(:review, rating: r, api: @api)
      end 
      @api.average_rating.should eq(3)
    end
  end
end

# spec/factories.rb
FactoryGirl.define do
  factory :api do
    name 'Fake API'
    description 'It does something'
    category 'Fake category'
    url 'http://www.example.com'
  end

  factory :review do
    rating rand(1..5)
    thoughts "blah"
    api   #Association: belongs_to API
  end
end
于 2013-04-28T18:54:08.923 回答
0

我认为这个测试不需要 FactoryGirl。您并没有真正测试有关数据库的任何内容。您只想为您的对象设置状态,然后测试该方法的结果。

根据您设置测试的方式,我认为您走在正确的轨道上。使用 RSpec 2.13 和 Ruby 1.9.3,这就是我编写测试的方式。

describe Api do

  subject(:api) { Api.new name: 'API Under Test', description: 'Testing' }

  describe "requesting the average rating" do
    context "with no reviews" do
      it { expect(api.average_rating).to be_nil }
    end

    context "with one or more reviews" do
      it "is the standard average of the review ratings rounded down" do
        api.reviews = [
          Review.new(rating: 1, thoughts: 'test review 1'),
          Review.new(rating: 2, thoughts: 'test review 2'),
          Review.new(rating: 5, thoughts: 'test review 3'),
        ]

        expect(api.average_rating).to eq 2
      end
    end
  end

end

几个跟进点:

  • 您不必使用 inner context,但是,我这样做是为了在设置上提供更多语义(而不是您期望测试证明的内容);context== 设置,it== 测试结果
  • 从 RSpec 2.11 开始,您可以使用命名主题而不仅仅是let. 这里的优点是您为您的测试对象提供语义线索
  • let并命名subject为您提供了一种定义记忆变量以跨测试使用的方法
  • 我远离@api实例变量的版本,因为它在不更新所有测试的情况下为以后的更改提供了更大的灵活性(参见barewords
  • 有关某些 RSpec 约定的更多信息,请参阅http://betterspecs.org/
于 2013-04-28T22:23:32.103 回答
0

如果您不想安装和设置 factorygirl(我建议您将来使用它,它在测试和开发控制台中都非常有用,它可以大大简化创建具有许多必需属性的对象) ,这里有一些代码。这也使用了新的 rspec 语法,其中 'expect' 优于 'should'。

describe "API" do
  describe "Method: average_rating" do
    before(:each) do
      @api = Api.create(:attributes => "some value")
    end

    it "is nil when there are no reviews" do
      expect( @api.average_rating ).to be_nil
    end

    it "calculates average rating properly" do 
      ratings_sum = 0
      5.times do |rating_value|
        @api.ratings << Rating.new(:rating => rating_value, :thoughts => "lorem ipsum")
        ratings_sum += rating_value
      end
      ratings_average = ratings_sum / @api.ratings.count
      expect( @api.average_rating ).to eq( ratings_average )
    end
  end
end
于 2013-04-28T19:42:32.603 回答