【问题标题】:RSpec: DRY way to test a set of valuesRSpec:测试一组值的 DRY 方式
【发布时间】:2011-04-03 02:44:26
【问题描述】:

我有一个vote 模型,它有一个名为score 的类方法。基本上,我在电子表格中创建了一个数学方程,并试图在 ruby​​ 中重现它。但是,我的第一次尝试不起作用,所以我真的需要开始添加更多测试。

我想进行测试的方法是从我的电子表格中获取一堆输入和输出值并分别测试它们。所以基本上,测试可以解释为如下所示:

 inputs = [a,b,c] ... score.should == x
 inputs = [a,b,c,d] ... score.should == y
 inputs = [c,d] .... score.should == z

但是,我实际上发现在 RSpec 中编写此代码的最短方法是为每种情况提供一个示例,大致如下(简化示例,但应该给你一个想法):

it "should have a score of X" do
  test_object = Votable.new(...)
  @user1.vote.create(:value=>##, :votable=>test_object)
  @user2.vote.create(:value=>##, :votable=>test_object)
  @user3.vote.create(:value=>##, :votable=>test_object)
  test_object.votes.score.should == X
end

所以,上述方法有效,但它为每个示例案例加载了大量文本,为了消除问题并提供良好的测试覆盖率,我想运行大约 20 个左右的测试案例。

所以,说真的,必须有一种更简单的方法来设置一次,然后测试一堆可能的输入/输出组合,对吧?任何人都可以建议在 RSpec 中进行这种测试的 DRY 方法吗?

谢谢!

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 refactoring rspec dry


    【解决方案1】:

    是的,您可以执行以下元编程来运行一系列都遵循相同格式的测试:

    results = { x: ['a', 'b', 'c'], y: ['a','b','c','d'] }
    
    results.each do |score, values|
      it "should have a score of #{score}" do
        test_object = Votable.new(...)
        values.each do |value|
          User.create(...).vote.create(value: value, votable: test_object)
        end
        test_object.votes.score.should == score
      end
    end
    

    【讨论】:

      【解决方案2】:

      @潘托马科斯:

      您的回答启发了我(所以我接受了它!)但实际上我根据您上面的建议创造了一些不同的东西。我对它感到非常满意,我想我会分享它以防它对其他人有益。

      以前我的模型有这个方法:

      def self.score
        dd = where( :value => -2 ).count.to_f
        d = where( :value => -1 ).count.to_f
        u = where( :value => 1 ).count.to_f
        uu = where( :value => 2 ).count.to_f
        tot = dd + d + u + uu
        score = (((-5*dd)+(-2*d)+(2*u)+(5*uu))/(tot+4))*20
        score.round(2)
      end
      

      这行得通,但它需要从数据库中计算选票,查看每个可能值(-2、-1、+1、+2)的选票计数,然后根据这些计数计算得分。

      由于我需要测试的不是 ActiveRecord 查找和计算查询结果的能力,而是我将这些计数转化为分数的算法,因此我将其分为两种方法,如下所示:

      def self.score
        dd = where( :value => -2 ).count
        d = where( :value => -1 ).count
        u = where( :value => 1 ).count
        uu = where( :value => 2 ).count
      
        self.compute_score(dd,d,u,uu)
      end
      
      def self.compute_score(dd, d, u, uu)
        tot = [dd,d,u,uu].sum.to_f
        score = [-5*dd, -2*d, 2*u, 5*uu].sum / [tot,4].sum*20.0
        score.round(2)
      end
      

      所以现在我可以只测试compute_score 方法,而无需创建一堆假用户和假投票来测试算法。我的测试现在看起来像:

      describe "score computation" do
        def test_score(a,b,c,d,e)
          Vote.compute_score(a,b,c,d).should == e
        end
      
        it "should be correct" do
          test_score(1,0,0,0,-20.0)
          test_score(0,1,0,0,-8.0)
          test_score(0,0,1,0,8.0)
          test_score(0,0,0,1,20.0)
      
          test_score(0,0,10,100,91.23)
          test_score(0,6,60,600,92.78)
          test_score(0,20,200,2000,93.17)
        end
      end
      

      在我看来,这是超级易读的,如果我向 RSpec 询问格式化输出,它对于它正在测试的内容来说已经足够好了。

      希望这项技术对其他人有用!

      【讨论】:

      • IMO 选择的答案更干净
      猜你喜欢
      • 1970-01-01
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-25
      • 1970-01-01
      • 2023-03-18
      • 2015-11-18
      • 1970-01-01
      相关资源
      最近更新 更多